cpu_switch.S revision 125530
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 125530 2004-02-06 20:38:39Z 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#ifdef SMP 52#define LK lock ; 53#else 54#define LK 55#endif 56 57/* 58 * cpu_throw() 59 * 60 * This is the second half of cpu_swtch(). It is used when the current 61 * thread is either a dummy or slated to die, and we no longer care 62 * about its state. This is only a slight optimization and is probably 63 * not worth it anymore. Note that we need to clear the pm_active bits so 64 * we do need the old proc if it still exists. 65 * %rdi = oldtd 66 * %rsi = newtd 67 */ 68ENTRY(cpu_throw) 69 movl PCPU(CPUID), %eax 70 testq %rdi,%rdi /* no thread? */ 71 jz 1f 72 /* release bit from old pm_active */ 73 movq TD_PROC(%rdi), %rdx /* oldtd->td_proc */ 74 movq P_VMSPACE(%rdx), %rdx /* proc->p_vmspace */ 75 LK btrl %eax, VM_PMAP+PM_ACTIVE(%rdx) /* clear old */ 761: 77 movq TD_PCB(%rsi),%rdx /* newtd->td_proc */ 78 movq PCB_CR3(%rdx),%rdx 79 movq %rdx,%cr3 /* new address space */ 80 /* set bit in new pm_active */ 81 movq TD_PROC(%rsi),%rdx 82 movq P_VMSPACE(%rdx), %rdx 83 LK 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 /* Switch to new thread. First, save context. */ 96 movq TD_PCB(%rdi),%r8 97 98 movq (%rsp),%rax /* Hardware registers */ 99 movq %rax,PCB_RIP(%r8) 100 movq %rbx,PCB_RBX(%r8) 101 movq %rsp,PCB_RSP(%r8) 102 movq %rbp,PCB_RBP(%r8) 103 movq %r12,PCB_R12(%r8) 104 movq %r13,PCB_R13(%r8) 105 movq %r14,PCB_R14(%r8) 106 movq %r15,PCB_R15(%r8) 107 pushfq /* PSL */ 108 popq PCB_RFLAGS(%r8) 109 110 /* Save userland %fs */ 111 movl $MSR_FSBASE,%ecx 112 rdmsr 113 movl %eax,PCB_FSBASE(%r8) 114 movl %edx,PCB_FSBASE+4(%r8) 115 116 /* Save userland %gs */ 117 movl $MSR_KGSBASE,%ecx 118 rdmsr 119 movl %eax,PCB_GSBASE(%r8) 120 movl %edx,PCB_GSBASE+4(%r8) 121 122 /* Save segment selector numbers */ 123 movl %ds,PCB_DS(%r8) 124 movl %es,PCB_ES(%r8) 125 movl %fs,PCB_FS(%r8) 126 movl %gs,PCB_GS(%r8) 127 128 /* Test if debug registers should be saved. */ 129 testl $PCB_DBREGS,PCB_FLAGS(%r8) 130 jz 1f /* no, skip over */ 131 movq %dr7,%rax /* yes, do the save */ 132 movq %rax,PCB_DR7(%r8) 133 andq $0x0000fc00, %rax /* disable all watchpoints */ 134 movq %rax,%dr7 135 movq %dr6,%rax 136 movq %rax,PCB_DR6(%r8) 137 movq %dr3,%rax 138 movq %rax,PCB_DR3(%r8) 139 movq %dr2,%rax 140 movq %rax,PCB_DR2(%r8) 141 movq %dr1,%rax 142 movq %rax,PCB_DR1(%r8) 143 movq %dr0,%rax 144 movq %rax,PCB_DR0(%r8) 1451: 146 147 /* have we used fp, and need a save? */ 148 cmpq %rdi,PCPU(FPCURTHREAD) 149 jne 1f 150 addq $PCB_SAVEFPU,%r8 151 clts 152 fxsave (%r8) 153 smsw %ax 154 orb $CR0_TS,%al 155 lmsw %ax 156 xorq %rax,%rax 157 movq %rax,PCPU(FPCURTHREAD) 1581: 159 160 /* Save is done. Now fire up new thread. Leave old vmspace. */ 161 movq TD_PCB(%rsi),%r8 162 163 /* switch address space */ 164 movq PCB_CR3(%r8),%rdx 165#ifdef LAZY_SWITCH 166 cmpq %rdx,KPML4phys /* Kernel address space? */ 167 je sw1 168#endif 169 movq %cr3,%rax 170 cmpq %rdx,%rax /* Same address space? */ 171 je sw1 172 movq %rdx,%cr3 /* new address space */ 173 174 movl PCPU(CPUID), %eax 175 /* Release bit from old pmap->pm_active */ 176 movq TD_PROC(%rdi), %rdx /* oldproc */ 177 movq P_VMSPACE(%rdx), %rdx 178 LK btrl %eax, VM_PMAP+PM_ACTIVE(%rdx) /* clear old */ 179 180 /* Set bit in new pmap->pm_active */ 181 movq TD_PROC(%rsi),%rdx /* newproc */ 182 movq P_VMSPACE(%rdx), %rdx 183 LK btsl %eax, VM_PMAP+PM_ACTIVE(%rdx) /* set new */ 184 185sw1: 186 /* 187 * At this point, we've switched address spaces and are ready 188 * to load up the rest of the next context. 189 */ 190 movq TD_PCB(%rsi),%r8 191 192 /* Restore segment selector numbers */ 193 movl PCB_DS(%r8),%ds 194 movl PCB_ES(%r8),%es 195 movl PCB_FS(%r8),%fs 196 197 /* Restore userland %gs while preserving kernel gsbase */ 198 movl $MSR_GSBASE,%ecx 199 rdmsr 200 movl PCB_GS(%r8),%gs 201 wrmsr 202 203 /* Restore userland %fs */ 204 movl $MSR_FSBASE,%ecx 205 movl PCB_FSBASE(%r8),%eax 206 movl PCB_FSBASE+4(%r8),%edx 207 wrmsr 208 209 /* Restore userland %gs */ 210 movl $MSR_KGSBASE,%ecx 211 movl PCB_GSBASE(%r8),%eax 212 movl PCB_GSBASE+4(%r8),%edx 213 wrmsr 214 215 /* Update the TSS_RSP0 pointer for the next interrupt */ 216 movq PCPU(TSSP), %rax 217 addq $COMMON_TSS_RSP0, %rax 218 leaq -16(%r8), %rbx 219 movq %rbx, (%rax) 220 movq %rbx, PCPU(RSP0) 221 222 /* Restore context. */ 223 movq PCB_RBX(%r8),%rbx 224 movq PCB_RSP(%r8),%rsp 225 movq PCB_RBP(%r8),%rbp 226 movq PCB_R12(%r8),%r12 227 movq PCB_R13(%r8),%r13 228 movq PCB_R14(%r8),%r14 229 movq PCB_R15(%r8),%r15 230 movq PCB_RIP(%r8),%rax 231 movq %rax,(%rsp) 232 pushq PCB_RFLAGS(%r8) 233 popfq 234 235 movq %r8, PCPU(CURPCB) 236 movq %rsi, PCPU(CURTHREAD) /* into next thread */ 237 238 /* Test if debug registers should be restored. */ 239 testl $PCB_DBREGS,PCB_FLAGS(%r8) 240 jz 1f 241 movq PCB_DR6(%r8),%rax 242 movq %rax,%dr6 243 movq PCB_DR3(%r8),%rax 244 movq %rax,%dr3 245 movq PCB_DR2(%r8),%rax 246 movq %rax,%dr2 247 movq PCB_DR1(%r8),%rax 248 movq %rax,%dr1 249 movq PCB_DR0(%r8),%rax 250 movq %rax,%dr0 251 /* But preserve reserved bits in %dr7 */ 252 movq %dr7,%rax 253 andq $0x0000fc00,%rax 254 movq PCB_DR7(%r8),%rcx 255 andq $~0x0000fc00,%rcx 256 orq %rcx,%rax 257 movq %rax,%dr7 2581: 259 260 ret 261 262/* 263 * savectx(pcb) 264 * Update pcb, saving current processor state. 265 */ 266ENTRY(savectx) 267 /* Fetch PCB. */ 268 movq %rdi,%rcx 269 270 /* Save caller's return address. */ 271 movq (%rsp),%rax 272 movq %rax,PCB_RIP(%rcx) 273 274 movq %cr3,%rax 275 movq %rax,PCB_CR3(%rcx) 276 277 movq %rbx,PCB_RBX(%rcx) 278 movq %rsp,PCB_RSP(%rcx) 279 movq %rbp,PCB_RBP(%rcx) 280 movq %r12,PCB_R12(%rcx) 281 movq %r13,PCB_R13(%rcx) 282 movq %r14,PCB_R14(%rcx) 283 movq %r15,PCB_R15(%rcx) 284 pushfq 285 popq PCB_RFLAGS(%rcx) 286 287 /* 288 * If fpcurthread == NULL, then the fpu h/w state is irrelevant and the 289 * state had better already be in the pcb. This is true for forks 290 * but not for dumps (the old book-keeping with FP flags in the pcb 291 * always lost for dumps because the dump pcb has 0 flags). 292 * 293 * If fpcurthread != NULL, then we have to save the fpu h/w state to 294 * fpcurthread's pcb and copy it to the requested pcb, or save to the 295 * requested pcb and reload. Copying is easier because we would 296 * have to handle h/w bugs for reloading. We used to lose the 297 * parent's fpu state for forks by forgetting to reload. 298 */ 299 pushfq 300 cli 301 movq PCPU(FPCURTHREAD),%rax 302 testq %rax,%rax 303 je 1f 304 305 movq TD_PCB(%rax),%rdi 306 leaq PCB_SAVEFPU(%rdi),%rdi 307 clts 308 fxsave (%rdi) 309 smsw %ax 310 orb $CR0_TS,%al 311 lmsw %ax 312 313 movq $PCB_SAVEFPU_SIZE,%rdx /* arg 3 */ 314 leaq PCB_SAVEFPU(%rcx),%rsi /* arg 2 */ 315 /* arg 1 (%rdi) already loaded */ 316 call bcopy 3171: 318 popfq 319 320 ret 321