cpu_switch.S revision 66855
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 66855 2000-10-09 05:39:03Z bde $ 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#include <machine/ipl.h> 46 47#ifdef SMP 48#include <machine/pmap.h> 49#include <machine/apic.h> 50#include <machine/smptests.h> /** GRAB_LOPRIO */ 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 _panic 64 65#if defined(SWTCH_OPTIM_STATS) 66 .globl _swtch_optim_stats, _tlb_flush_count 67_swtch_optim_stats: .long 0 /* number of _swtch_optims */ 68_tlb_flush_count: .long 0 69#endif 70 71 .text 72 73/* 74 * cpu_throw() 75 */ 76ENTRY(cpu_throw) 77 jmp sw1 78 79/* 80 * cpu_switch() 81 */ 82ENTRY(cpu_switch) 83 84 /* switch to new process. first, save context as needed */ 85 movl _curproc,%ecx 86 87 /* if no process to save, don't bother */ 88 testl %ecx,%ecx 89 jz sw1 90 91#ifdef SMP 92 movb P_ONCPU(%ecx), %al /* save "last" cpu */ 93 movb %al, P_LASTCPU(%ecx) 94 movb $0xff, P_ONCPU(%ecx) /* "leave" the cpu */ 95#endif /* SMP */ 96 movl P_VMSPACE(%ecx), %edx 97#ifdef SMP 98 movl _cpuid, %eax 99#else 100 xorl %eax, %eax 101#endif /* SMP */ 102 btrl %eax, VM_PMAP+PM_ACTIVE(%edx) 103 104 movl P_ADDR(%ecx),%edx 105 106 movl (%esp),%eax /* Hardware registers */ 107 movl %eax,PCB_EIP(%edx) 108 movl %ebx,PCB_EBX(%edx) 109 movl %esp,PCB_ESP(%edx) 110 movl %ebp,PCB_EBP(%edx) 111 movl %esi,PCB_ESI(%edx) 112 movl %edi,PCB_EDI(%edx) 113 movl %gs,PCB_GS(%edx) 114 115 /* test if debug registers should be saved */ 116 movb PCB_FLAGS(%edx),%al 117 andb $PCB_DBREGS,%al 118 jz 1f /* no, skip over */ 119 movl %dr7,%eax /* yes, do the save */ 120 movl %eax,PCB_DR7(%edx) 121 andl $0x0000ff00, %eax /* disable all watchpoints */ 122 movl %eax,%dr7 123 movl %dr6,%eax 124 movl %eax,PCB_DR6(%edx) 125 movl %dr3,%eax 126 movl %eax,PCB_DR3(%edx) 127 movl %dr2,%eax 128 movl %eax,PCB_DR2(%edx) 129 movl %dr1,%eax 130 movl %eax,PCB_DR1(%edx) 131 movl %dr0,%eax 132 movl %eax,PCB_DR0(%edx) 1331: 134 135 /* save sched_lock recursion count */ 136 movl _sched_lock+MTX_RECURSE,%eax 137 movl %eax,PCB_SCHEDNEST(%edx) 138 139#ifdef SMP 140 /* XXX FIXME: we should be saving the local APIC TPR */ 141#endif /* SMP */ 142 143#if NNPX > 0 144 /* have we used fp, and need a save? */ 145 cmpl %ecx,_npxproc 146 jne 1f 147 addl $PCB_SAVEFPU,%edx /* h/w bugs make saving complicated */ 148 pushl %edx 149 call _npxsave /* do it in a big C function */ 150 popl %eax 1511: 152#endif /* NNPX > 0 */ 153 154 /* save is done, now choose a new process */ 155sw1: 156 157#ifdef SMP 158 /* Stop scheduling if smp_active goes zero and we are not BSP */ 159 cmpl $0,_smp_active 160 jne 1f 161 cmpl $0,_cpuid 162 je 1f 163 164 movl _idleproc, %eax 165 jmp sw1b 1661: 167#endif 168 169 /* 170 * Choose a new process to schedule. chooseproc() returns idleproc 171 * if it cannot find another process to run. 172 */ 173sw1a: 174 call _chooseproc /* trash ecx, edx, ret eax*/ 175 176#ifdef DIAGNOSTIC 177 testl %eax,%eax /* no process? */ 178 jz badsw3 /* no, panic */ 179#endif 180sw1b: 181 movl %eax,%ecx 182 183 xorl %eax,%eax 184 andl $~AST_RESCHED,_astpending 185 186#ifdef DIAGNOSTIC 187 cmpl %eax,P_WCHAN(%ecx) 188 jne badsw1 189 cmpb $SRUN,P_STAT(%ecx) 190 jne badsw2 191#endif 192 193 movl P_ADDR(%ecx),%edx 194 195#if defined(SWTCH_OPTIM_STATS) 196 incl _swtch_optim_stats 197#endif 198 /* switch address space */ 199 movl %cr3,%ebx 200 cmpl PCB_CR3(%edx),%ebx 201 je 4f 202#if defined(SWTCH_OPTIM_STATS) 203 decl _swtch_optim_stats 204 incl _tlb_flush_count 205#endif 206 movl PCB_CR3(%edx),%ebx 207 movl %ebx,%cr3 2084: 209 210#ifdef SMP 211 movl _cpuid, %esi 212#else 213 xorl %esi, %esi 214#endif 215 cmpl $0, PCB_EXT(%edx) /* has pcb extension? */ 216 je 1f 217 btsl %esi, _private_tss /* mark use of private tss */ 218 movl PCB_EXT(%edx), %edi /* new tss descriptor */ 219 jmp 2f 2201: 221 222 /* update common_tss.tss_esp0 pointer */ 223 movl %edx, %ebx /* pcb */ 224 addl $(UPAGES * PAGE_SIZE - 16), %ebx 225 movl %ebx, _common_tss + TSS_ESP0 226 227 btrl %esi, _private_tss 228 jae 3f 229#ifdef SMP 230 movl $gd_common_tssd, %edi 231 addl %fs:0, %edi 232#else 233 movl $_common_tssd, %edi 234#endif 2352: 236 /* move correct tss descriptor into GDT slot, then reload tr */ 237 movl _tss_gdt, %ebx /* entry in GDT */ 238 movl 0(%edi), %eax 239 movl %eax, 0(%ebx) 240 movl 4(%edi), %eax 241 movl %eax, 4(%ebx) 242 movl $GPROC0_SEL*8, %esi /* GSEL(entry, SEL_KPL) */ 243 ltr %si 2443: 245 movl P_VMSPACE(%ecx), %ebx 246#ifdef SMP 247 movl _cpuid, %eax 248#else 249 xorl %eax, %eax 250#endif 251 btsl %eax, VM_PMAP+PM_ACTIVE(%ebx) 252 253 /* restore context */ 254 movl PCB_EBX(%edx),%ebx 255 movl PCB_ESP(%edx),%esp 256 movl PCB_EBP(%edx),%ebp 257 movl PCB_ESI(%edx),%esi 258 movl PCB_EDI(%edx),%edi 259 movl PCB_EIP(%edx),%eax 260 movl %eax,(%esp) 261 262#ifdef SMP 263#ifdef GRAB_LOPRIO /* hold LOPRIO for INTs */ 264#ifdef CHEAP_TPR 265 movl $0, lapic_tpr 266#else 267 andl $~APIC_TPR_PRIO, lapic_tpr 268#endif /** CHEAP_TPR */ 269#endif /** GRAB_LOPRIO */ 270 movl _cpuid,%eax 271 movb %al, P_ONCPU(%ecx) 272#endif /* SMP */ 273 movl %edx, _curpcb 274 movl %ecx, _curproc /* into next process */ 275 276#ifdef SMP 277 /* XXX FIXME: we should be restoring the local APIC TPR */ 278#endif /* SMP */ 279 280#ifdef USER_LDT 281 cmpl $0, PCB_USERLDT(%edx) 282 jnz 1f 283 movl __default_ldt,%eax 284 cmpl _currentldt,%eax 285 je 2f 286 lldt __default_ldt 287 movl %eax,_currentldt 288 jmp 2f 2891: pushl %edx 290 call _set_user_ldt 291 popl %edx 2922: 293#endif 294 295 /* This must be done after loading the user LDT. */ 296 .globl cpu_switch_load_gs 297cpu_switch_load_gs: 298 movl PCB_GS(%edx),%gs 299 300 /* test if debug regisers should be restored */ 301 movb PCB_FLAGS(%edx),%al 302 andb $PCB_DBREGS,%al 303 jz 1f /* no, skip over */ 304 movl PCB_DR6(%edx),%eax /* yes, do the restore */ 305 movl %eax,%dr6 306 movl PCB_DR3(%edx),%eax 307 movl %eax,%dr3 308 movl PCB_DR2(%edx),%eax 309 movl %eax,%dr2 310 movl PCB_DR1(%edx),%eax 311 movl %eax,%dr1 312 movl PCB_DR0(%edx),%eax 313 movl %eax,%dr0 314 movl PCB_DR7(%edx),%eax 315 movl %eax,%dr7 3161: 317 318 /* 319 * restore sched_lock recursion count and transfer ownership to 320 * new process 321 */ 322 movl PCB_SCHEDNEST(%edx),%eax 323 movl %eax,_sched_lock+MTX_RECURSE 324 325 movl _curproc,%eax 326 movl %eax,_sched_lock+MTX_LOCK 327 328#ifdef DIAGNOSTIC 329 pushfl 330 popl %ecx 331 testl $0x200, %ecx /* interrupts enabled? */ 332 jnz badsw6 /* that way madness lies */ 333#endif 334 ret 335 336CROSSJUMPTARGET(sw1a) 337 338#ifdef DIAGNOSTIC 339badsw1: 340 pushl $sw0_1 341 call _panic 342 343sw0_1: .asciz "cpu_switch: has wchan" 344 345badsw2: 346 pushl $sw0_2 347 call _panic 348 349sw0_2: .asciz "cpu_switch: not SRUN" 350 351badsw3: 352 pushl $sw0_3 353 call _panic 354 355sw0_3: .asciz "cpu_switch: chooseproc returned NULL" 356 357#endif 358 359#ifdef DIAGNOSTIC 360badsw5: 361 pushl $sw0_5 362 call _panic 363 364sw0_5: .asciz "cpu_switch: interrupts enabled (again)" 365badsw6: 366 pushl $sw0_6 367 call _panic 368 369sw0_6: .asciz "cpu_switch: interrupts enabled" 370#endif 371 372/* 373 * savectx(pcb) 374 * Update pcb, saving current processor state. 375 */ 376ENTRY(savectx) 377 /* fetch PCB */ 378 movl 4(%esp),%ecx 379 380 /* caller's return address - child won't execute this routine */ 381 movl (%esp),%eax 382 movl %eax,PCB_EIP(%ecx) 383 384 movl %ebx,PCB_EBX(%ecx) 385 movl %esp,PCB_ESP(%ecx) 386 movl %ebp,PCB_EBP(%ecx) 387 movl %esi,PCB_ESI(%ecx) 388 movl %edi,PCB_EDI(%ecx) 389 movl %gs,PCB_GS(%ecx) 390 391#if NNPX > 0 392 /* 393 * If npxproc == NULL, then the npx h/w state is irrelevant and the 394 * state had better already be in the pcb. This is true for forks 395 * but not for dumps (the old book-keeping with FP flags in the pcb 396 * always lost for dumps because the dump pcb has 0 flags). 397 * 398 * If npxproc != NULL, then we have to save the npx h/w state to 399 * npxproc's pcb and copy it to the requested pcb, or save to the 400 * requested pcb and reload. Copying is easier because we would 401 * have to handle h/w bugs for reloading. We used to lose the 402 * parent's npx state for forks by forgetting to reload. 403 */ 404 movl _npxproc,%eax 405 testl %eax,%eax 406 je 1f 407 408 pushl %ecx 409 movl P_ADDR(%eax),%eax 410 leal PCB_SAVEFPU(%eax),%eax 411 pushl %eax 412 pushl %eax 413 call _npxsave 414 addl $4,%esp 415 popl %eax 416 popl %ecx 417 418 pushl $PCB_SAVEFPU_SIZE 419 leal PCB_SAVEFPU(%ecx),%ecx 420 pushl %ecx 421 pushl %eax 422 call _bcopy 423 addl $12,%esp 424#endif /* NNPX > 0 */ 425 4261: 427 ret 428