cpu_switch.S revision 69779
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 69779 2000-12-08 21:31:52Z jake $ 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 INVARIANTS 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 INVARIANTS 187 cmpb $SRUN,P_STAT(%ecx) 188 jne badsw2 189#endif 190 191 movl P_ADDR(%ecx),%edx 192 193#if defined(SWTCH_OPTIM_STATS) 194 incl _swtch_optim_stats 195#endif 196 /* switch address space */ 197 movl %cr3,%ebx 198 cmpl PCB_CR3(%edx),%ebx 199 je 4f 200#if defined(SWTCH_OPTIM_STATS) 201 decl _swtch_optim_stats 202 incl _tlb_flush_count 203#endif 204 movl PCB_CR3(%edx),%ebx 205 movl %ebx,%cr3 2064: 207 208#ifdef SMP 209 movl _cpuid, %esi 210#else 211 xorl %esi, %esi 212#endif 213 cmpl $0, PCB_EXT(%edx) /* has pcb extension? */ 214 je 1f 215 btsl %esi, _private_tss /* mark use of private tss */ 216 movl PCB_EXT(%edx), %edi /* new tss descriptor */ 217 jmp 2f 2181: 219 220 /* update common_tss.tss_esp0 pointer */ 221 movl %edx, %ebx /* pcb */ 222 addl $(UPAGES * PAGE_SIZE - 16), %ebx 223 movl %ebx, _common_tss + TSS_ESP0 224 225 btrl %esi, _private_tss 226 jae 3f 227#ifdef SMP 228 movl $gd_common_tssd, %edi 229 addl %fs:0, %edi 230#else 231 movl $_common_tssd, %edi 232#endif 2332: 234 /* move correct tss descriptor into GDT slot, then reload tr */ 235 movl _tss_gdt, %ebx /* entry in GDT */ 236 movl 0(%edi), %eax 237 movl %eax, 0(%ebx) 238 movl 4(%edi), %eax 239 movl %eax, 4(%ebx) 240 movl $GPROC0_SEL*8, %esi /* GSEL(entry, SEL_KPL) */ 241 ltr %si 2423: 243 movl P_VMSPACE(%ecx), %ebx 244#ifdef SMP 245 movl _cpuid, %eax 246#else 247 xorl %eax, %eax 248#endif 249 btsl %eax, VM_PMAP+PM_ACTIVE(%ebx) 250 251 /* restore context */ 252 movl PCB_EBX(%edx),%ebx 253 movl PCB_ESP(%edx),%esp 254 movl PCB_EBP(%edx),%ebp 255 movl PCB_ESI(%edx),%esi 256 movl PCB_EDI(%edx),%edi 257 movl PCB_EIP(%edx),%eax 258 movl %eax,(%esp) 259 260#ifdef SMP 261#ifdef GRAB_LOPRIO /* hold LOPRIO for INTs */ 262#ifdef CHEAP_TPR 263 movl $0, lapic_tpr 264#else 265 andl $~APIC_TPR_PRIO, lapic_tpr 266#endif /** CHEAP_TPR */ 267#endif /** GRAB_LOPRIO */ 268 movl _cpuid,%eax 269 movb %al, P_ONCPU(%ecx) 270#endif /* SMP */ 271 movl %edx, _curpcb 272 movl %ecx, _curproc /* into next process */ 273 274#ifdef SMP 275 /* XXX FIXME: we should be restoring the local APIC TPR */ 276#endif /* SMP */ 277 278#ifdef USER_LDT 279 cmpl $0, PCB_USERLDT(%edx) 280 jnz 1f 281 movl __default_ldt,%eax 282 cmpl _currentldt,%eax 283 je 2f 284 lldt __default_ldt 285 movl %eax,_currentldt 286 jmp 2f 2871: pushl %edx 288 call _set_user_ldt 289 popl %edx 2902: 291#endif 292 293 /* This must be done after loading the user LDT. */ 294 .globl cpu_switch_load_gs 295cpu_switch_load_gs: 296 movl PCB_GS(%edx),%gs 297 298 /* test if debug regisers should be restored */ 299 movb PCB_FLAGS(%edx),%al 300 andb $PCB_DBREGS,%al 301 jz 1f /* no, skip over */ 302 movl PCB_DR6(%edx),%eax /* yes, do the restore */ 303 movl %eax,%dr6 304 movl PCB_DR3(%edx),%eax 305 movl %eax,%dr3 306 movl PCB_DR2(%edx),%eax 307 movl %eax,%dr2 308 movl PCB_DR1(%edx),%eax 309 movl %eax,%dr1 310 movl PCB_DR0(%edx),%eax 311 movl %eax,%dr0 312 movl PCB_DR7(%edx),%eax 313 movl %eax,%dr7 3141: 315 316 /* 317 * restore sched_lock recursion count and transfer ownership to 318 * new process 319 */ 320 movl PCB_SCHEDNEST(%edx),%eax 321 movl %eax,_sched_lock+MTX_RECURSE 322 323 movl _curproc,%eax 324 movl %eax,_sched_lock+MTX_LOCK 325 326 ret 327 328CROSSJUMPTARGET(sw1a) 329 330#ifdef INVARIANTS 331badsw2: 332 pushl $sw0_2 333 call _panic 334 335sw0_2: .asciz "cpu_switch: not SRUN" 336 337badsw3: 338 pushl $sw0_3 339 call _panic 340 341sw0_3: .asciz "cpu_switch: chooseproc returned NULL" 342#endif 343 344/* 345 * savectx(pcb) 346 * Update pcb, saving current processor state. 347 */ 348ENTRY(savectx) 349 /* fetch PCB */ 350 movl 4(%esp),%ecx 351 352 /* caller's return address - child won't execute this routine */ 353 movl (%esp),%eax 354 movl %eax,PCB_EIP(%ecx) 355 356 movl %cr3,%eax 357 movl %eax,PCB_CR3(%ecx) 358 359 movl %ebx,PCB_EBX(%ecx) 360 movl %esp,PCB_ESP(%ecx) 361 movl %ebp,PCB_EBP(%ecx) 362 movl %esi,PCB_ESI(%ecx) 363 movl %edi,PCB_EDI(%ecx) 364 movl %gs,PCB_GS(%ecx) 365 366#if NNPX > 0 367 /* 368 * If npxproc == NULL, then the npx h/w state is irrelevant and the 369 * state had better already be in the pcb. This is true for forks 370 * but not for dumps (the old book-keeping with FP flags in the pcb 371 * always lost for dumps because the dump pcb has 0 flags). 372 * 373 * If npxproc != NULL, then we have to save the npx h/w state to 374 * npxproc's pcb and copy it to the requested pcb, or save to the 375 * requested pcb and reload. Copying is easier because we would 376 * have to handle h/w bugs for reloading. We used to lose the 377 * parent's npx state for forks by forgetting to reload. 378 */ 379 movl _npxproc,%eax 380 testl %eax,%eax 381 je 1f 382 383 pushl %ecx 384 movl P_ADDR(%eax),%eax 385 leal PCB_SAVEFPU(%eax),%eax 386 pushl %eax 387 pushl %eax 388 call _npxsave 389 addl $4,%esp 390 popl %eax 391 popl %ecx 392 393 pushl $PCB_SAVEFPU_SIZE 394 leal PCB_SAVEFPU(%ecx),%ecx 395 pushl %ecx 396 pushl %eax 397 call _bcopy 398 addl $12,%esp 399#endif /* NNPX > 0 */ 400 4011: 402 ret 403