cpu_switch.S revision 151429
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 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * $FreeBSD: head/sys/amd64/amd64/cpu_switch.S 151429 2005-10-17 23:10:31Z davidxu $ 34 */ 35 36#include <machine/asmacros.h> 37#include <machine/specialreg.h> 38 39#include "assym.s" 40 41/*****************************************************************************/ 42/* Scheduling */ 43/*****************************************************************************/ 44 45 .text 46 47#ifdef SMP 48#define LK lock ; 49#else 50#define LK 51#endif 52 53/* 54 * cpu_throw() 55 * 56 * This is the second half of cpu_switch(). It is used when the current 57 * thread is either a dummy or slated to die, and we no longer care 58 * about its state. This is only a slight optimization and is probably 59 * not worth it anymore. Note that we need to clear the pm_active bits so 60 * we do need the old proc if it still exists. 61 * %rdi = oldtd 62 * %rsi = newtd 63 */ 64ENTRY(cpu_throw) 65 movl PCPU(CPUID), %eax 66 testq %rdi,%rdi /* no thread? */ 67 jz 1f 68 /* release bit from old pm_active */ 69 movq TD_PROC(%rdi), %rdx /* oldtd->td_proc */ 70 movq P_VMSPACE(%rdx), %rdx /* proc->p_vmspace */ 71 LK btrl %eax, VM_PMAP+PM_ACTIVE(%rdx) /* clear old */ 721: 73 movq TD_PCB(%rsi),%rdx /* newtd->td_proc */ 74 movq PCB_CR3(%rdx),%rdx 75 movq %rdx,%cr3 /* new address space */ 76 /* set bit in new pm_active */ 77 movq TD_PROC(%rsi),%rdx 78 movq P_VMSPACE(%rdx), %rdx 79 LK btsl %eax, VM_PMAP+PM_ACTIVE(%rdx) /* set new */ 80 jmp sw1 81 82/* 83 * cpu_switch(old, new) 84 * 85 * Save the current thread state, then select the next thread to run 86 * and load its state. 87 * %rdi = oldtd 88 * %rsi = newtd 89 */ 90ENTRY(cpu_switch) 91 /* Switch to new thread. First, save context. */ 92 movq TD_PCB(%rdi),%r8 93 94 movq (%rsp),%rax /* Hardware registers */ 95 movq %rax,PCB_RIP(%r8) 96 movq %rbx,PCB_RBX(%r8) 97 movq %rsp,PCB_RSP(%r8) 98 movq %rbp,PCB_RBP(%r8) 99 movq %r12,PCB_R12(%r8) 100 movq %r13,PCB_R13(%r8) 101 movq %r14,PCB_R14(%r8) 102 movq %r15,PCB_R15(%r8) 103 104 testl $PCB_32BIT,PCB_FLAGS(%r8) 105 jz 1f /* no, skip over */ 106 107 /* Save segment selector numbers */ 108 movl %ds,PCB_DS(%r8) 109 movl %es,PCB_ES(%r8) 110 movl %fs,PCB_FS(%r8) 111 movl %gs,PCB_GS(%r8) 1121: 113 /* Test if debug registers should be saved. */ 114 testl $PCB_DBREGS,PCB_FLAGS(%r8) 115 jz 1f /* no, skip over */ 116 movq %dr7,%rax /* yes, do the save */ 117 movq %rax,PCB_DR7(%r8) 118 andq $0x0000fc00, %rax /* disable all watchpoints */ 119 movq %rax,%dr7 120 movq %dr6,%rax 121 movq %rax,PCB_DR6(%r8) 122 movq %dr3,%rax 123 movq %rax,PCB_DR3(%r8) 124 movq %dr2,%rax 125 movq %rax,PCB_DR2(%r8) 126 movq %dr1,%rax 127 movq %rax,PCB_DR1(%r8) 128 movq %dr0,%rax 129 movq %rax,PCB_DR0(%r8) 1301: 131 132 /* have we used fp, and need a save? */ 133 cmpq %rdi,PCPU(FPCURTHREAD) 134 jne 1f 135 addq $PCB_SAVEFPU,%r8 136 clts 137 fxsave (%r8) 138 smsw %ax 139 orb $CR0_TS,%al 140 lmsw %ax 141 xorl %eax,%eax 142 movq %rax,PCPU(FPCURTHREAD) 1431: 144 145 /* Save is done. Now fire up new thread. Leave old vmspace. */ 146 movq TD_PCB(%rsi),%r8 147 148 /* switch address space */ 149 movq PCB_CR3(%r8),%rdx 150 movq %cr3,%rax 151 cmpq %rdx,%rax /* Same address space? */ 152 je sw1 153 movq %rdx,%cr3 /* new address space */ 154 155 movl PCPU(CPUID), %eax 156 /* Release bit from old pmap->pm_active */ 157 movq TD_PROC(%rdi), %rdx /* oldproc */ 158 movq P_VMSPACE(%rdx), %rdx 159 LK btrl %eax, VM_PMAP+PM_ACTIVE(%rdx) /* clear old */ 160 161 /* Set bit in new pmap->pm_active */ 162 movq TD_PROC(%rsi),%rdx /* newproc */ 163 movq P_VMSPACE(%rdx), %rdx 164 LK btsl %eax, VM_PMAP+PM_ACTIVE(%rdx) /* set new */ 165 166sw1: 167 /* 168 * At this point, we've switched address spaces and are ready 169 * to load up the rest of the next context. 170 */ 171 movq TD_PCB(%rsi),%r8 172 173 testl $PCB_32BIT,PCB_FLAGS(%r8) 174 jz 1f /* no, skip over */ 175 176 /* Restore segment selector numbers */ 177 movl PCB_DS(%r8),%ds 178 movl PCB_ES(%r8),%es 179 movl PCB_FS(%r8),%fs 180 181 /* Restore userland %gs while preserving kernel gsbase */ 182 movl $MSR_GSBASE,%ecx 183 rdmsr 184 movl PCB_GS(%r8),%gs 185 wrmsr 186 jmp 2f 1871: 188 189 /* Restore userland %fs */ 190 movl $MSR_FSBASE,%ecx 191 movl PCB_FSBASE(%r8),%eax 192 movl PCB_FSBASE+4(%r8),%edx 193 wrmsr 194 195 /* Restore userland %gs */ 196 movl $MSR_KGSBASE,%ecx 197 movl PCB_GSBASE(%r8),%eax 198 movl PCB_GSBASE+4(%r8),%edx 199 wrmsr 2002: 201 202 /* Update the TSS_RSP0 pointer for the next interrupt */ 203 movq PCPU(TSSP), %rax 204 addq $COMMON_TSS_RSP0, %rax 205 leaq -16(%r8), %rbx 206 movq %rbx, (%rax) 207 movq %rbx, PCPU(RSP0) 208 209 /* Restore context. */ 210 movq PCB_RBX(%r8),%rbx 211 movq PCB_RSP(%r8),%rsp 212 movq PCB_RBP(%r8),%rbp 213 movq PCB_R12(%r8),%r12 214 movq PCB_R13(%r8),%r13 215 movq PCB_R14(%r8),%r14 216 movq PCB_R15(%r8),%r15 217 movq PCB_RIP(%r8),%rax 218 movq %rax,(%rsp) 219 220 movq %r8, PCPU(CURPCB) 221 movq %rsi, PCPU(CURTHREAD) /* into next thread */ 222 223 /* Test if debug registers should be restored. */ 224 testl $PCB_DBREGS,PCB_FLAGS(%r8) 225 jz 1f 226 movq PCB_DR6(%r8),%rax 227 movq %rax,%dr6 228 movq PCB_DR3(%r8),%rax 229 movq %rax,%dr3 230 movq PCB_DR2(%r8),%rax 231 movq %rax,%dr2 232 movq PCB_DR1(%r8),%rax 233 movq %rax,%dr1 234 movq PCB_DR0(%r8),%rax 235 movq %rax,%dr0 236 /* But preserve reserved bits in %dr7 */ 237 movq %dr7,%rax 238 andq $0x0000fc00,%rax 239 movq PCB_DR7(%r8),%rcx 240 andq $~0x0000fc00,%rcx 241 orq %rcx,%rax 242 movq %rax,%dr7 2431: 244 ret 245 246/* 247 * savectx(pcb) 248 * Update pcb, saving current processor state. 249 */ 250ENTRY(savectx) 251 /* Fetch PCB. */ 252 movq %rdi,%rcx 253 254 /* Save caller's return address. */ 255 movq (%rsp),%rax 256 movq %rax,PCB_RIP(%rcx) 257 258 movq %cr3,%rax 259 movq %rax,PCB_CR3(%rcx) 260 261 movq %rbx,PCB_RBX(%rcx) 262 movq %rsp,PCB_RSP(%rcx) 263 movq %rbp,PCB_RBP(%rcx) 264 movq %r12,PCB_R12(%rcx) 265 movq %r13,PCB_R13(%rcx) 266 movq %r14,PCB_R14(%rcx) 267 movq %r15,PCB_R15(%rcx) 268 269 /* 270 * If fpcurthread == NULL, then the fpu h/w state is irrelevant and the 271 * state had better already be in the pcb. This is true for forks 272 * but not for dumps (the old book-keeping with FP flags in the pcb 273 * always lost for dumps because the dump pcb has 0 flags). 274 * 275 * If fpcurthread != NULL, then we have to save the fpu h/w state to 276 * fpcurthread's pcb and copy it to the requested pcb, or save to the 277 * requested pcb and reload. Copying is easier because we would 278 * have to handle h/w bugs for reloading. We used to lose the 279 * parent's fpu state for forks by forgetting to reload. 280 */ 281 pushfq 282 cli 283 movq PCPU(FPCURTHREAD),%rax 284 testq %rax,%rax 285 je 1f 286 287 movq TD_PCB(%rax),%rdi 288 leaq PCB_SAVEFPU(%rdi),%rdi 289 clts 290 fxsave (%rdi) 291 smsw %ax 292 orb $CR0_TS,%al 293 lmsw %ax 294 295 movq $PCB_SAVEFPU_SIZE,%rdx /* arg 3 */ 296 leaq PCB_SAVEFPU(%rcx),%rsi /* arg 2 */ 297 /* arg 1 (%rdi) already loaded */ 298 call bcopy 2991: 300 popfq 301 302 ret 303