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