cpu_switch.S revision 125181
1/*- 2 * Copyright (c) 2003 Peter Wemm. 3 * Copyright (c) 1990 The Regents of the University of California. 4 * All rights reserved. 5 * 6 * This code is derived from software contributed to Berkeley by 7 * William Jolitz. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by the University of 20 * California, Berkeley and its contributors. 21 * 4. Neither the name of the University nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 * 37 * $FreeBSD: head/sys/amd64/amd64/cpu_switch.S 125181 2004-01-29 00:02:54Z peter $ 38 */ 39 40#include <machine/asmacros.h> 41#include <machine/specialreg.h> 42 43#include "assym.s" 44 45/*****************************************************************************/ 46/* Scheduling */ 47/*****************************************************************************/ 48 49 .text 50 51/* 52 * cpu_throw() 53 * 54 * This is the second half of cpu_swtch(). It is used when the current 55 * thread is either a dummy or slated to die, and we no longer care 56 * about its state. This is only a slight optimization and is probably 57 * not worth it anymore. Note that we need to clear the pm_active bits so 58 * we do need the old proc if it still exists. 59 * %rdi = oldtd 60 * %rsi = newtd 61 */ 62ENTRY(cpu_throw) 63 movl PCPU(CPUID), %eax 64 testq %rdi,%rdi /* no thread? */ 65 jz 1f 66 /* release bit from old pm_active */ 67 movq TD_PROC(%rdi), %rdx /* oldtd->td_proc */ 68 movq P_VMSPACE(%rdx), %rdx /* proc->p_vmspace */ 69#ifdef SMP 70 lock 71#endif 72 btrl %eax, VM_PMAP+PM_ACTIVE(%rdx) /* clear old */ 731: 74 movq TD_PCB(%rsi),%rdx /* newtd->td_proc */ 75 movq PCB_CR3(%rdx),%rdx 76 movq %rdx,%cr3 /* new address space */ 77 /* set bit in new pm_active */ 78 movq TD_PROC(%rsi),%rdx 79 movq P_VMSPACE(%rdx), %rdx 80#ifdef SMP 81 lock 82#endif 83 btsl %eax, VM_PMAP+PM_ACTIVE(%rdx) /* set new */ 84 jmp sw1 85 86/* 87 * cpu_switch(old, new) 88 * 89 * Save the current thread state, then select the next thread to run 90 * and load its state. 91 * %rdi = oldtd 92 * %rsi = newtd 93 */ 94ENTRY(cpu_switch) 95 96 /* Switch to new thread. First, save context. */ 97#ifdef INVARIANTS 98 testq %rdi,%rdi /* no thread? */ 99 jz badsw2 /* no, panic */ 100#endif 101 102 movq TD_PCB(%rdi),%r8 103 104 movq (%rsp),%rax /* Hardware registers */ 105 movq %rax,PCB_RIP(%r8) 106 movq %rbx,PCB_RBX(%r8) 107 movq %rsp,PCB_RSP(%r8) 108 movq %rbp,PCB_RBP(%r8) 109 movq %r12,PCB_R12(%r8) 110 movq %r13,PCB_R13(%r8) 111 movq %r14,PCB_R14(%r8) 112 movq %r15,PCB_R15(%r8) 113 pushfq /* PSL */ 114 popq PCB_RFLAGS(%r8) 115 116 /* Save userland %fs */ 117 movl $MSR_FSBASE,%ecx 118 rdmsr 119 movl %eax,PCB_FSBASE(%r8) 120 movl %edx,PCB_FSBASE+4(%r8) 121 122 /* Save userland %gs */ 123 movl $MSR_KGSBASE,%ecx 124 rdmsr 125 movl %eax,PCB_GSBASE(%r8) 126 movl %edx,PCB_GSBASE+4(%r8) 127 128 /* Save segment selector numbers */ 129 movl %ds,PCB_DS(%r8) 130 movl %es,PCB_ES(%r8) 131 movl %fs,PCB_FS(%r8) 132 movl %gs,PCB_GS(%r8) 133 134 /* Test if debug registers should be saved. */ 135 testl $PCB_DBREGS,PCB_FLAGS(%r8) 136 jz 1f /* no, skip over */ 137 movq %dr7,%rax /* yes, do the save */ 138 movq %rax,PCB_DR7(%r8) 139 andq $0x0000fc00, %rax /* disable all watchpoints */ 140 movq %rax,%dr7 141 movq %dr6,%rax 142 movq %rax,PCB_DR6(%r8) 143 movq %dr3,%rax 144 movq %rax,PCB_DR3(%r8) 145 movq %dr2,%rax 146 movq %rax,PCB_DR2(%r8) 147 movq %dr1,%rax 148 movq %rax,PCB_DR1(%r8) 149 movq %dr0,%rax 150 movq %rax,PCB_DR0(%r8) 1511: 152 153 /* have we used fp, and need a save? */ 154 cmpq %rdi,PCPU(FPCURTHREAD) 155 jne 1f 156 addq $PCB_SAVEFPU,%r8 157 clts 158 fxsave (%r8) 159 smsw %ax 160 orb $CR0_TS,%al 161 lmsw %ax 162 xorq %rax,%rax 163 movq %rax,PCPU(FPCURTHREAD) 1641: 165 166 /* Save is done. Now fire up new thread. Leave old vmspace. */ 167#ifdef INVARIANTS 168 testq %rsi,%rsi /* no thread? */ 169 jz badsw3 /* no, panic */ 170#endif 171 movq TD_PCB(%rsi),%r8 172 173 /* switch address space */ 174 movq PCB_CR3(%r8),%rdx 175#ifdef LAZY_SWITCH 176 cmpq %rdx,KPML4phys /* Kernel address space? */ 177 je sw1 178#endif 179 movq %cr3,%rax 180 cmpq %rdx,%rax /* Same address space? */ 181 je sw1 182 movq %rdx,%cr3 /* new address space */ 183 184 movl PCPU(CPUID), %eax 185 /* Release bit from old pmap->pm_active */ 186 movq TD_PROC(%rdi), %rdx /* oldproc */ 187 movq P_VMSPACE(%rdx), %rdx 188#ifdef SMP 189 lock 190#endif 191 btrl %eax, VM_PMAP+PM_ACTIVE(%rdx) /* clear old */ 192 193 /* Set bit in new pmap->pm_active */ 194 movq TD_PROC(%rsi),%rdx /* newproc */ 195 movq P_VMSPACE(%rdx), %rdx 196#ifdef SMP 197 lock 198#endif 199 btsl %eax, VM_PMAP+PM_ACTIVE(%rdx) /* set new */ 200 201sw1: 202 /* 203 * At this point, we've switched address spaces and are ready 204 * to load up the rest of the next context. 205 */ 206 movq TD_PCB(%rsi),%r8 207 208 /* Restore segment selector numbers */ 209 movl PCB_DS(%r8),%ds 210 movl PCB_ES(%r8),%es 211 movl PCB_FS(%r8),%fs 212 213 /* Restore userland %gs while preserving kernel gsbase */ 214 movl $MSR_GSBASE,%ecx 215 rdmsr 216 movl PCB_GS(%r8),%gs 217 wrmsr 218 219 /* Restore userland %fs */ 220 movl $MSR_FSBASE,%ecx 221 movl PCB_FSBASE(%r8),%eax 222 movl PCB_FSBASE+4(%r8),%edx 223 wrmsr 224 225 /* Restore userland %gs */ 226 movl $MSR_KGSBASE,%ecx 227 movl PCB_GSBASE(%r8),%eax 228 movl PCB_GSBASE+4(%r8),%edx 229 wrmsr 230 231 /* Update the TSS_RSP0 pointer for the next interrupt */ 232 movq PCPU(TSSP), %rax 233 addq $COMMON_TSS_RSP0, %rax 234 leaq -16(%r8), %rbx 235 movq %rbx, (%rax) 236 movq %rbx, PCPU(RSP0) 237 238 /* Restore context. */ 239 movq PCB_RBX(%r8),%rbx 240 movq PCB_RSP(%r8),%rsp 241 movq PCB_RBP(%r8),%rbp 242 movq PCB_R12(%r8),%r12 243 movq PCB_R13(%r8),%r13 244 movq PCB_R14(%r8),%r14 245 movq PCB_R15(%r8),%r15 246 movq PCB_RIP(%r8),%rax 247 movq %rax,(%rsp) 248 pushq PCB_RFLAGS(%r8) 249 popfq 250 251 movq %r8, PCPU(CURPCB) 252 movq %rsi, PCPU(CURTHREAD) /* into next thread */ 253 254 /* Test if debug registers should be restored. */ 255 testl $PCB_DBREGS,PCB_FLAGS(%r8) 256 jz 1f 257 movq PCB_DR6(%r8),%rax 258 movq %rax,%dr6 259 movq PCB_DR3(%r8),%rax 260 movq %rax,%dr3 261 movq PCB_DR2(%r8),%rax 262 movq %rax,%dr2 263 movq PCB_DR1(%r8),%rax 264 movq %rax,%dr1 265 movq PCB_DR0(%r8),%rax 266 movq %rax,%dr0 267 /* But preserve reserved bits in %dr7 */ 268 movq %dr7,%rax 269 andq $0x0000fc00,%rax 270 movq PCB_DR7(%r8),%rcx 271 andq $~0x0000fc00,%rcx 272 orq %rcx,%rax 273 movq %rax,%dr7 2741: 275 276 ret 277 278#ifdef INVARIANTS 279badsw1: 280 pushq %rax 281 pushq %rcx 282 pushq %rdx 283 pushq %rbx 284 pushq %rbp 285 pushq %rsi 286 pushq %rdi 287 pushq %r8 288 pushq %r9 289 pushq %r10 290 pushq %r11 291 pushq %r12 292 pushq %r13 293 pushq %r14 294 pushq %r15 295 movq $0,%rdi 296 movq $0,%rsi 297 leaq sw0_1,%rdx 298 call __panic 299sw0_1: .asciz "cpu_throw: no newthread supplied" 300 301badsw2: 302 pushq %rax 303 pushq %rcx 304 pushq %rdx 305 pushq %rbx 306 pushq %rbp 307 pushq %rsi 308 pushq %rdi 309 pushq %r8 310 pushq %r9 311 pushq %r10 312 pushq %r11 313 pushq %r12 314 pushq %r13 315 pushq %r14 316 pushq %r15 317 movq $0,%rdi 318 movq $0,%rsi 319 leaq sw0_2,%rdx 320 call __panic 321sw0_2: .asciz "cpu_switch: no curthread supplied" 322 323badsw3: 324 pushq %rax 325 pushq %rcx 326 pushq %rdx 327 pushq %rbx 328 pushq %rbp 329 pushq %rsi 330 pushq %rdi 331 pushq %r8 332 pushq %r9 333 pushq %r10 334 pushq %r11 335 pushq %r12 336 pushq %r13 337 pushq %r14 338 pushq %r15 339 movq $0,%rdi 340 movq $0,%rsi 341 leaq sw0_3,%rdx 342 call __panic 343sw0_3: .asciz "cpu_switch: no newthread supplied" 344#endif 345 346noswitch: .asciz "cpu_switch: called!" 347nothrow: .asciz "cpu_throw: called!" 348/* 349 * savectx(pcb) 350 * Update pcb, saving current processor state. 351 */ 352ENTRY(savectx) 353 /* Fetch PCB. */ 354 movq %rdi,%rcx 355 356 /* Save caller's return address. */ 357 movq (%rsp),%rax 358 movq %rax,PCB_RIP(%rcx) 359 360 movq %cr3,%rax 361 movq %rax,PCB_CR3(%rcx) 362 363 movq %rbx,PCB_RBX(%rcx) 364 movq %rsp,PCB_RSP(%rcx) 365 movq %rbp,PCB_RBP(%rcx) 366 movq %r12,PCB_R12(%rcx) 367 movq %r13,PCB_R13(%rcx) 368 movq %r14,PCB_R14(%rcx) 369 movq %r15,PCB_R15(%rcx) 370 pushfq 371 popq PCB_RFLAGS(%rcx) 372 373 /* 374 * If fpcurthread == NULL, then the fpu h/w state is irrelevant and the 375 * state had better already be in the pcb. This is true for forks 376 * but not for dumps (the old book-keeping with FP flags in the pcb 377 * always lost for dumps because the dump pcb has 0 flags). 378 * 379 * If fpcurthread != NULL, then we have to save the fpu h/w state to 380 * fpcurthread's pcb and copy it to the requested pcb, or save to the 381 * requested pcb and reload. Copying is easier because we would 382 * have to handle h/w bugs for reloading. We used to lose the 383 * parent's fpu state for forks by forgetting to reload. 384 */ 385 pushfq 386 cli 387 movq PCPU(FPCURTHREAD),%rax 388 testq %rax,%rax 389 je 1f 390 391 movq TD_PCB(%rax),%rdi 392 leaq PCB_SAVEFPU(%rdi),%rdi 393 clts 394 fxsave (%rdi) 395 smsw %ax 396 orb $CR0_TS,%al 397 lmsw %ax 398 399 movq $PCB_SAVEFPU_SIZE,%rdx /* arg 3 */ 400 leaq PCB_SAVEFPU(%rcx),%rsi /* arg 2 */ 401 /* arg 1 (%rdi) already loaded */ 402 call bcopy 4031: 404 popfq 405 406 ret 407