cpu_switch.S revision 85487
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 85487 2001-10-25 16:47:01Z jhb $ 37 */ 38 39#include "opt_npx.h" 40 41#include <machine/asmacros.h> 42 43#ifdef SMP 44#include <machine/apic.h> 45#include <machine/smptests.h> /* CHEAP_TPR, GRAB_LOPRIO */ 46#endif /* SMP */ 47 48#include "assym.s" 49 50/*****************************************************************************/ 51/* Scheduling */ 52/*****************************************************************************/ 53 54 .data 55 56 .globl panic 57 58#if defined(SWTCH_OPTIM_STATS) 59 .globl swtch_optim_stats, tlb_flush_count 60swtch_optim_stats: .long 0 /* number of _swtch_optims */ 61tlb_flush_count: .long 0 62#endif 63 64 .text 65 66/* 67 * cpu_throw() 68 */ 69ENTRY(cpu_throw) 70 jmp sw1 71 72/* 73 * cpu_switch() 74 */ 75ENTRY(cpu_switch) 76 77 /* Switch to new process. First, save context as needed. */ 78 movl PCPU(CURTHREAD),%ecx 79 80 /* If no process to save, don't save it (XXX shouldn't happen). */ 81 testl %ecx,%ecx 82 jz sw1 83 84 movl TD_PROC(%ecx), %eax 85 movl P_VMSPACE(%eax), %edx 86 movl PCPU(CPUID), %eax 87 btrl %eax, VM_PMAP+PM_ACTIVE(%edx) 88 89 movl TD_PCB(%ecx),%edx 90 91 movl (%esp),%eax /* Hardware registers */ 92 movl %eax,PCB_EIP(%edx) 93 movl %ebx,PCB_EBX(%edx) 94 movl %esp,PCB_ESP(%edx) 95 movl %ebp,PCB_EBP(%edx) 96 movl %esi,PCB_ESI(%edx) 97 movl %edi,PCB_EDI(%edx) 98 movl %gs,PCB_GS(%edx) 99 100 /* Test if debug registers should be saved. */ 101 movb PCB_FLAGS(%edx),%al 102 andb $PCB_DBREGS,%al 103 jz 1f /* no, skip over */ 104 movl %dr7,%eax /* yes, do the save */ 105 movl %eax,PCB_DR7(%edx) 106 andl $0x0000fc00, %eax /* disable all watchpoints */ 107 movl %eax,%dr7 108 movl %dr6,%eax 109 movl %eax,PCB_DR6(%edx) 110 movl %dr3,%eax 111 movl %eax,PCB_DR3(%edx) 112 movl %dr2,%eax 113 movl %eax,PCB_DR2(%edx) 114 movl %dr1,%eax 115 movl %eax,PCB_DR1(%edx) 116 movl %dr0,%eax 117 movl %eax,PCB_DR0(%edx) 1181: 119 120#ifdef SMP 121 /* XXX FIXME: we should be saving the local APIC TPR */ 122#endif /* SMP */ 123 124#ifdef DEV_NPX 125 /* have we used fp, and need a save? */ 126 cmpl %ecx,PCPU(NPXTHREAD) 127 jne 1f 128 addl $PCB_SAVEFPU,%edx /* h/w bugs make saving complicated */ 129 pushl %edx 130 call npxsave /* do it in a big C function */ 131 popl %eax 1321: 133#endif /* DEV_NPX */ 134 135 /* Save is done. Now choose a new thread. */ 136 /* XXX still trashing space above the old "Top Of Stack". */ 137sw1: 138 139#ifdef SMP 140 /* Stop scheduling if smp_active goes to zero and we are not the BSP. */ 141 cmpl $0,smp_active 142 jne 1f 143 cmpl $0,PCPU(CPUID) 144 je 1f 145 /* Idle thread can run on any kernel context. */ 146 movl PCPU(IDLETHREAD), %eax 147 jmp sw1b 1481: 149#endif 150 151 /* 152 * Choose a new thread to schedule. choosethread() returns idlethread 153 * if it cannot find another thread to run. 154 */ 155sw1a: 156 call choosethread /* trash ecx, edx, ret eax */ 157 158#ifdef INVARIANTS 159 testl %eax,%eax /* no thread? */ 160 jz badsw3 /* no, panic */ 161#endif 162 163sw1b: 164 movl %eax,%ecx 165 166#ifdef INVARIANTS 167 movl TD_PROC(%ecx), %eax /* XXXKSE */ 168 cmpb $SRUN,P_STAT(%eax) 169 jne badsw2 170#endif 171 172 movl TD_PCB(%ecx),%edx 173 174#if defined(SWTCH_OPTIM_STATS) 175 incl swtch_optim_stats 176#endif 177 178 /* switch address space */ 179 movl %cr3,%ebx 180 cmpl PCB_CR3(%edx),%ebx 181 je 4f 182#if defined(SWTCH_OPTIM_STATS) 183 decl swtch_optim_stats 184 incl tlb_flush_count 185#endif 186 movl PCB_CR3(%edx),%ebx 187 movl %ebx,%cr3 /* Load new page tables. */ 1884: 189 190 movl PCPU(CPUID), %esi 191 cmpl $0, PCB_EXT(%edx) /* has pcb extension? */ 192 je 1f 193 btsl %esi, private_tss /* mark use of private tss */ 194 movl PCB_EXT(%edx), %edi /* new tss descriptor */ 195 jmp 2f 1961: 197 /* update common_tss.tss_esp0 pointer */ 198 leal -16(%edx), %ebx /* leave space for vm86 */ 199 movl %ebx, PCPU(COMMON_TSS) + TSS_ESP0 /* stack is below pcb */ 200 201 btrl %esi, private_tss 202 jae 3f 203 PCPU_ADDR(COMMON_TSSD, %edi) 2042: 205 /* Move correct tss descriptor into GDT slot, then reload tr. */ 206 movl PCPU(TSS_GDT), %ebx /* entry in GDT */ 207 movl 0(%edi), %eax 208 movl %eax, 0(%ebx) 209 movl 4(%edi), %eax 210 movl %eax, 4(%ebx) 211 movl $GPROC0_SEL*8, %esi /* GSEL(entry, SEL_KPL) */ 212 ltr %si 2133: 214 /* Note in a vmspace that this cpu is using it. */ 215 movl TD_PROC(%ecx),%eax /* XXXKSE proc from thread */ 216 movl P_VMSPACE(%eax), %ebx 217 movl PCPU(CPUID), %eax 218 btsl %eax, VM_PMAP+PM_ACTIVE(%ebx) 219 220 /* restore context */ 221 movl PCB_EBX(%edx),%ebx 222 movl PCB_ESP(%edx),%esp 223 movl PCB_EBP(%edx),%ebp 224 movl PCB_ESI(%edx),%esi 225 movl PCB_EDI(%edx),%edi 226 movl PCB_EIP(%edx),%eax 227 movl %eax,(%esp) 228 229#ifdef SMP 230#ifdef GRAB_LOPRIO /* hold LOPRIO for INTs */ 231#ifdef CHEAP_TPR 232 movl $0, lapic+LA_TPR 233#else 234 andl $~APIC_TPR_PRIO, lapic+LA_TPR 235#endif /** CHEAP_TPR */ 236#endif /** GRAB_LOPRIO */ 237#endif /* SMP */ 238 movl %edx, PCPU(CURPCB) 239 movl %ecx, PCPU(CURTHREAD) /* into next thread */ 240 241#ifdef SMP 242 /* XXX FIXME: we should be restoring the local APIC TPR */ 243#endif /* SMP */ 244 245 movl TD_PROC(%ecx),%eax 246 cmpl $0,P_MD+MD_LDT(%eax) /* Have an LDT? */ 247 jnz 1f /* Yes, use it. */ 248 movl _default_ldt,%eax /* Otherwise, use default. */ 249 cmpl PCPU(CURRENTLDT),%eax 250 je 2f 251 lldt _default_ldt 252 movl %eax,PCPU(CURRENTLDT) 253 jmp 2f 254 2551: pushl %edx /* Preserver pointer to pcb. */ 256 pushl P_MD+MD_LDT(%eax) /* passing p_md -> set_user_ldt */ 257 call set_user_ldt /* Check and load the ldt. */ 258 popl %eax 259 popl %edx 2602: 261 262 /* This must be done after loading the user LDT. */ 263 .globl cpu_switch_load_gs 264cpu_switch_load_gs: 265 movl PCB_GS(%edx),%gs 266 267 /* test if debug regisers should be restored */ 268 movb PCB_FLAGS(%edx),%al 269 andb $PCB_DBREGS,%al 270 jz 1f /* no, skip over */ 271 movl PCB_DR6(%edx),%eax /* yes, do the restore */ 272 movl %eax,%dr6 273 movl PCB_DR3(%edx),%eax 274 movl %eax,%dr3 275 movl PCB_DR2(%edx),%eax 276 movl %eax,%dr2 277 movl PCB_DR1(%edx),%eax 278 movl %eax,%dr1 279 movl PCB_DR0(%edx),%eax 280 movl %eax,%dr0 281 movl %dr7,%eax /* load dr7 so as not to */ 282 andl $0x0000fc00,%eax /* disturb reserved bits */ 283 pushl %ebx 284 movl PCB_DR7(%edx),%ebx 285 andl $~0x0000fc00,%ebx /* re-enable the restored watchpoints */ 286 orl %ebx,%eax 287 popl %ebx 288 movl %eax,%dr7 2891: 290 ret 291 292CROSSJUMPTARGET(sw1a) 293 294#ifdef INVARIANTS 295badsw2: 296 pushl $sw0_2 297 call panic 298 299sw0_2: .asciz "cpu_switch: not SRUN" 300 301badsw3: 302 pushl $sw0_3 303 call panic 304 305sw0_3: .asciz "cpu_switch: choosethread returned NULL" 306#endif 307 308/* 309 * savectx(pcb) 310 * Update pcb, saving current processor state. 311 */ 312ENTRY(savectx) 313 /* fetch PCB */ 314 movl 4(%esp),%ecx 315 316 /* caller's return address - child won't execute this routine */ 317 movl (%esp),%eax 318 movl %eax,PCB_EIP(%ecx) 319 320 movl %cr3,%eax 321 movl %eax,PCB_CR3(%ecx) 322 323 movl %ebx,PCB_EBX(%ecx) 324 movl %esp,PCB_ESP(%ecx) 325 movl %ebp,PCB_EBP(%ecx) 326 movl %esi,PCB_ESI(%ecx) 327 movl %edi,PCB_EDI(%ecx) 328 movl %gs,PCB_GS(%ecx) 329 330#ifdef DEV_NPX 331 /* 332 * If npxthread == NULL, then the npx h/w state is irrelevant and the 333 * state had better already be in the pcb. This is true for forks 334 * but not for dumps (the old book-keeping with FP flags in the pcb 335 * always lost for dumps because the dump pcb has 0 flags). 336 * 337 * If npxthread != NULL, then we have to save the npx h/w state to 338 * npxthread's pcb and copy it to the requested pcb, or save to the 339 * requested pcb and reload. Copying is easier because we would 340 * have to handle h/w bugs for reloading. We used to lose the 341 * parent's npx state for forks by forgetting to reload. 342 */ 343 pushfl 344 cli 345 movl PCPU(NPXTHREAD),%eax 346 testl %eax,%eax 347 je 1f 348 349 pushl %ecx 350 movl TD_PCB(%eax),%eax 351 leal PCB_SAVEFPU(%eax),%eax 352 pushl %eax 353 pushl %eax 354 call npxsave 355 addl $4,%esp 356 popl %eax 357 popl %ecx 358 359 pushl $PCB_SAVEFPU_SIZE 360 leal PCB_SAVEFPU(%ecx),%ecx 361 pushl %ecx 362 pushl %eax 363 call bcopy 364 addl $12,%esp 3651: 366 popfl 367#endif /* DEV_NPX */ 368 369 ret 370