cpu_switch.S revision 150647
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 150647 2005-09-27 21:10:10Z peter $ 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) 112 jmp 2f 1131: 114 115 /* Save userland %fs */ 116 movl $MSR_FSBASE,%ecx 117 rdmsr 118 movl %eax,PCB_FSBASE(%r8) 119 movl %edx,PCB_FSBASE+4(%r8) 120 121 /* Save userland %gs */ 122 movl $MSR_KGSBASE,%ecx 123 rdmsr 124 movl %eax,PCB_GSBASE(%r8) 125 movl %edx,PCB_GSBASE+4(%r8) 1262: 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 xorl %eax,%eax 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 movq %cr3,%rax 166 cmpq %rdx,%rax /* Same address space? */ 167 je sw1 168 movq %rdx,%cr3 /* new address space */ 169 170 movl PCPU(CPUID), %eax 171 /* Release bit from old pmap->pm_active */ 172 movq TD_PROC(%rdi), %rdx /* oldproc */ 173 movq P_VMSPACE(%rdx), %rdx 174 LK btrl %eax, VM_PMAP+PM_ACTIVE(%rdx) /* clear old */ 175 176 /* Set bit in new pmap->pm_active */ 177 movq TD_PROC(%rsi),%rdx /* newproc */ 178 movq P_VMSPACE(%rdx), %rdx 179 LK btsl %eax, VM_PMAP+PM_ACTIVE(%rdx) /* set new */ 180 181sw1: 182 /* 183 * At this point, we've switched address spaces and are ready 184 * to load up the rest of the next context. 185 */ 186 movq TD_PCB(%rsi),%r8 187 188 testl $PCB_32BIT,PCB_FLAGS(%r8) 189 jz 1f /* no, skip over */ 190 191 /* Restore segment selector numbers */ 192 movl PCB_DS(%r8),%ds 193 movl PCB_ES(%r8),%es 194 movl PCB_FS(%r8),%fs 195 196 /* Restore userland %gs while preserving kernel gsbase */ 197 movl $MSR_GSBASE,%ecx 198 rdmsr 199 movl PCB_GS(%r8),%gs 200 wrmsr 201 jmp 2f 2021: 203 204 /* Restore userland %fs */ 205 movl $MSR_FSBASE,%ecx 206 movl PCB_FSBASE(%r8),%eax 207 movl PCB_FSBASE+4(%r8),%edx 208 wrmsr 209 210 /* Restore userland %gs */ 211 movl $MSR_KGSBASE,%ecx 212 movl PCB_GSBASE(%r8),%eax 213 movl PCB_GSBASE+4(%r8),%edx 214 wrmsr 2152: 216 217 /* Update the TSS_RSP0 pointer for the next interrupt */ 218 movq PCPU(TSSP), %rax 219 addq $COMMON_TSS_RSP0, %rax 220 leaq -16(%r8), %rbx 221 movq %rbx, (%rax) 222 movq %rbx, PCPU(RSP0) 223 224 /* Restore context. */ 225 movq PCB_RBX(%r8),%rbx 226 movq PCB_RSP(%r8),%rsp 227 movq PCB_RBP(%r8),%rbp 228 movq PCB_R12(%r8),%r12 229 movq PCB_R13(%r8),%r13 230 movq PCB_R14(%r8),%r14 231 movq PCB_R15(%r8),%r15 232 movq PCB_RIP(%r8),%rax 233 movq %rax,(%rsp) 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 ret 260 261/* 262 * savectx(pcb) 263 * Update pcb, saving current processor state. 264 */ 265ENTRY(savectx) 266 /* Fetch PCB. */ 267 movq %rdi,%rcx 268 269 /* Save caller's return address. */ 270 movq (%rsp),%rax 271 movq %rax,PCB_RIP(%rcx) 272 273 movq %cr3,%rax 274 movq %rax,PCB_CR3(%rcx) 275 276 movq %rbx,PCB_RBX(%rcx) 277 movq %rsp,PCB_RSP(%rcx) 278 movq %rbp,PCB_RBP(%rcx) 279 movq %r12,PCB_R12(%rcx) 280 movq %r13,PCB_R13(%rcx) 281 movq %r14,PCB_R14(%rcx) 282 movq %r15,PCB_R15(%rcx) 283 284 /* 285 * If fpcurthread == NULL, then the fpu h/w state is irrelevant and the 286 * state had better already be in the pcb. This is true for forks 287 * but not for dumps (the old book-keeping with FP flags in the pcb 288 * always lost for dumps because the dump pcb has 0 flags). 289 * 290 * If fpcurthread != NULL, then we have to save the fpu h/w state to 291 * fpcurthread's pcb and copy it to the requested pcb, or save to the 292 * requested pcb and reload. Copying is easier because we would 293 * have to handle h/w bugs for reloading. We used to lose the 294 * parent's fpu state for forks by forgetting to reload. 295 */ 296 pushfq 297 cli 298 movq PCPU(FPCURTHREAD),%rax 299 testq %rax,%rax 300 je 1f 301 302 movq TD_PCB(%rax),%rdi 303 leaq PCB_SAVEFPU(%rdi),%rdi 304 clts 305 fxsave (%rdi) 306 smsw %ax 307 orb $CR0_TS,%al 308 lmsw %ax 309 310 movq $PCB_SAVEFPU_SIZE,%rdx /* arg 3 */ 311 leaq PCB_SAVEFPU(%rcx),%rsi /* arg 2 */ 312 /* arg 1 (%rdi) already loaded */ 313 call bcopy 3141: 315 popfq 316 317 ret 318