swtch-v6.S revision 254847
1/* $NetBSD: cpuswitch.S,v 1.41 2003/11/15 08:44:18 scw Exp $ */ 2 3/*- 4 * Copyright 2003 Wasabi Systems, Inc. 5 * All rights reserved. 6 * 7 * Written by Steve C. Woodford for Wasabi Systems, Inc. 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 for the NetBSD Project by 20 * Wasabi Systems, Inc. 21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22 * or promote products derived from this software without specific prior 23 * written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37/*- 38 * Copyright (c) 1994-1998 Mark Brinicombe. 39 * Copyright (c) 1994 Brini. 40 * All rights reserved. 41 * 42 * This code is derived from software written for Brini by Mark Brinicombe 43 * 44 * Redistribution and use in source and binary forms, with or without 45 * modification, are permitted provided that the following conditions 46 * are met: 47 * 1. Redistributions of source code must retain the above copyright 48 * notice, this list of conditions and the following disclaimer. 49 * 2. Redistributions in binary form must reproduce the above copyright 50 * notice, this list of conditions and the following disclaimer in the 51 * documentation and/or other materials provided with the distribution. 52 * 3. All advertising materials mentioning features or use of this software 53 * must display the following acknowledgement: 54 * This product includes software developed by Brini. 55 * 4. The name of the company nor the name of the author may be used to 56 * endorse or promote products derived from this software without specific 57 * prior written permission. 58 * 59 * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED 60 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 61 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 62 * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 63 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 64 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 65 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 66 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 67 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 68 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 69 * SUCH DAMAGE. 70 * 71 * RiscBSD kernel project 72 * 73 * cpuswitch.S 74 * 75 * cpu switching functions 76 * 77 * Created : 15/10/94 78 * 79 */ 80 81#include "assym.s" 82#include "opt_sched.h" 83 84#include <machine/asm.h> 85#include <machine/asmacros.h> 86#include <machine/armreg.h> 87__FBSDID("$FreeBSD: head/sys/arm/arm/swtch.S 254847 2013-08-25 11:23:38Z andrew $"); 88 89#define DOMAIN_CLIENT 0x01 90 91#ifdef _ARM_ARCH_6 92#define GET_PCPU(tmp) \ 93 mrc p15, 0, tmp, c13, c0, 4; 94#else 95.Lcurpcpu: 96 .word _C_LABEL(__pcpu) 97 98#define GET_PCPU(tmp) \ 99 ldr tmp, .Lcurpcpu 100#endif 101 102.Lcpufuncs: 103 .word _C_LABEL(cpufuncs) 104.Lblocked_lock: 105 .word _C_LABEL(blocked_lock) 106 107ENTRY(cpu_throw) 108 mov r5, r1 109 110 /* 111 * r0 = oldtd 112 * r5 = newtd 113 */ 114 115 GET_PCPU(r7) 116 117#ifdef VFP 118 /* 119 * vfp_discard will clear pcpu->pc_vfpcthread, and modify 120 * and modify the control as needed. 121 */ 122 ldr r4, [r7, #(PC_VFPCTHREAD)] /* this thread using vfp? */ 123 cmp r0, r4 124 bne 3f 125 bl _C_LABEL(vfp_discard) /* yes, shut down vfp */ 1263: 127#endif /* VFP */ 128 129 ldr r7, [r5, #(TD_PCB)] /* r7 = new thread's PCB */ 130 131 /* Switch to lwp0 context */ 132 133 ldr r9, .Lcpufuncs 134#if !defined(CPU_ARM11) && !defined(CPU_CORTEXA) && !defined(CPU_MV_PJ4B) 135 mov lr, pc 136 ldr pc, [r9, #CF_IDCACHE_WBINV_ALL] 137#endif 138 ldr r0, [r7, #(PCB_PL1VEC)] 139 ldr r1, [r7, #(PCB_DACR)] 140 /* 141 * r0 = Pointer to L1 slot for vector_page (or NULL) 142 * r1 = lwp0's DACR 143 * r5 = lwp0 144 * r6 = exit func 145 * r7 = lwp0's PCB 146 * r9 = cpufuncs 147 */ 148 149 /* 150 * Ensure the vector table is accessible by fixing up lwp0's L1 151 */ 152 cmp r0, #0 /* No need to fixup vector table? */ 153 ldrne r3, [r0] /* But if yes, fetch current value */ 154 ldrne r2, [r7, #(PCB_L1VEC)] /* Fetch new vector_page value */ 155 mcr p15, 0, r1, c3, c0, 0 /* Update DACR for lwp0's context */ 156 cmpne r3, r2 /* Stuffing the same value? */ 157 strne r2, [r0] /* Store if not. */ 158 159#ifdef PMAP_INCLUDE_PTE_SYNC 160 /* 161 * Need to sync the cache to make sure that last store is 162 * visible to the MMU. 163 */ 164 movne r1, #4 165 movne lr, pc 166 ldrne pc, [r9, #CF_DCACHE_WB_RANGE] 167#endif /* PMAP_INCLUDE_PTE_SYNC */ 168 169 /* 170 * Note: We don't do the same optimisation as cpu_switch() with 171 * respect to avoiding flushing the TLB if we're switching to 172 * the same L1 since this process' VM space may be about to go 173 * away, so we don't want *any* turds left in the TLB. 174 */ 175 176 /* Switch the memory to the new process */ 177 ldr r0, [r7, #(PCB_PAGEDIR)] 178 mov lr, pc 179 ldr pc, [r9, #CF_CONTEXT_SWITCH] 180 181 /* Restore all the save registers */ 182#ifndef _ARM_ARCH_5E 183 add r1, r7, #PCB_R8 184 ldmia r1, {r8-r13} 185#else 186 ldr r8, [r7, #(PCB_R8)] 187 ldr r9, [r7, #(PCB_R9)] 188 ldr r10, [r7, #(PCB_R10)] 189 ldr r11, [r7, #(PCB_R11)] 190 ldr r12, [r7, #(PCB_R12)] 191 ldr r13, [r7, #(PCB_SP)] 192#endif 193 194 /* We have a new curthread now so make a note it */ 195 GET_CURTHREAD_PTR(r6) 196 str r5, [r6] 197 198 /* Set the new tp */ 199 ldr r6, [r5, #(TD_MD + MD_TP)] 200#ifdef ARM_TP_ADDRESS 201 ldr r4, =ARM_TP_ADDRESS 202 str r6, [r4] 203 ldr r6, [r5, #(TD_MD + MD_RAS_START)] 204 str r6, [r4, #4] /* ARM_RAS_START */ 205 ldr r6, [r5, #(TD_MD + MD_RAS_END)] 206 str r6, [r4, #8] /* ARM_RAS_END */ 207#else 208 mcr p15, 0, r6, c13, c0, 3 209#endif 210 /* Hook in a new pcb */ 211 GET_PCPU(r6) 212 str r7, [r6, #PC_CURPCB] 213 214 add sp, sp, #4; 215 ldmfd sp!, {r4-r7, pc} 216END(cpu_throw) 217 218ENTRY(cpu_switch) 219 stmfd sp!, {r4-r7, lr} 220 sub sp, sp, #4; 221#ifdef __ARM_EABI__ 222 .save {r4-r7, lr} 223 .pad #4 224#endif 225 226 mov r6, r2 /* Save the mutex */ 227 228.Lswitch_resume: 229 /* rem: r0 = old lwp */ 230 /* rem: interrupts are disabled */ 231 232 /* Process is now on a processor. */ 233 /* We have a new curthread now so make a note it */ 234 GET_CURTHREAD_PTR(r7) 235 str r1, [r7] 236 237 /* Hook in a new pcb */ 238 GET_PCPU(r7) 239 ldr r2, [r1, #TD_PCB] 240 str r2, [r7, #PC_CURPCB] 241 242 /* rem: r1 = new process */ 243 /* rem: interrupts are enabled */ 244 245 /* Stage two : Save old context */ 246 247 /* Get the user structure for the old thread. */ 248 ldr r2, [r0, #(TD_PCB)] 249 mov r4, r0 /* Save the old thread. */ 250 251 /* Save all the registers in the old thread's pcb */ 252#ifndef _ARM_ARCH_5E 253 add r7, r2, #(PCB_R8) 254 stmia r7, {r8-r13} 255#else 256 strd r8, [r2, #(PCB_R8)] 257 strd r10, [r2, #(PCB_R10)] 258 strd r12, [r2, #(PCB_R12)] 259#endif 260 str pc, [r2, #(PCB_PC)] 261 262 /* 263 * NOTE: We can now use r8-r13 until it is time to restore 264 * them for the new process. 265 */ 266#ifdef ARM_TP_ADDRESS 267 /* Store the old tp */ 268 ldr r3, =ARM_TP_ADDRESS 269 ldr r9, [r3] 270 str r9, [r0, #(TD_MD + MD_TP)] 271 ldr r9, [r3, #4] 272 str r9, [r0, #(TD_MD + MD_RAS_START)] 273 ldr r9, [r3, #8] 274 str r9, [r0, #(TD_MD + MD_RAS_END)] 275 276 /* Set the new tp */ 277 ldr r9, [r1, #(TD_MD + MD_TP)] 278 str r9, [r3] 279 ldr r9, [r1, #(TD_MD + MD_RAS_START)] 280 str r9, [r3, #4] 281 ldr r9, [r1, #(TD_MD + MD_RAS_END)] 282 str r9, [r3, #8] 283#else 284 /* Store the old tp */ 285 mrc p15, 0, r9, c13, c0, 3 286 str r9, [r0, #(TD_MD + MD_TP)] 287 288 /* Set the new tp */ 289 ldr r9, [r1, #(TD_MD + MD_TP)] 290 mcr p15, 0, r9, c13, c0, 3 291#endif 292 293 /* Get the user structure for the new process in r9 */ 294 ldr r9, [r1, #(TD_PCB)] 295 296 mrs r3, cpsr 297 /* 298 * We can do that, since 299 * PSR_SVC32_MODE|PSR_UND32_MODE == MSR_UND32_MODE 300 */ 301 orr r8, r3, #(PSR_UND32_MODE) 302 msr cpsr_c, r8 303 304 str sp, [r2, #(PCB_UND_SP)] 305 306 msr cpsr_c, r3 /* Restore the old mode */ 307 /* rem: r2 = old PCB */ 308 /* rem: r9 = new PCB */ 309 /* rem: interrupts are enabled */ 310 311#ifdef VFP 312 /* 313 * vfp_store will clear pcpu->pc_vfpcthread, save 314 * registers and state, and modify the control as needed. 315 * a future exception will bounce the backup settings in the fp unit. 316 * XXX vfp_store can't change r4 317 */ 318 GET_PCPU(r7) 319 ldr r8, [r7, #(PC_VFPCTHREAD)] 320 cmp r4, r8 /* old thread used vfp? */ 321 bne 1f /* no, don't save */ 322 cmp r1, r4 /* same thread ? */ 323 beq 1f /* yes, skip vfp store */ 324#ifdef SMP 325 ldr r8, [r7, #(PC_CPU)] /* last used on this cpu? */ 326 ldr r3, [r2, #(PCB_VFPCPU)] 327 cmp r8, r3 /* last cpu to use these registers? */ 328 bne 1f /* no. these values are stale */ 329#endif 330 add r0, r2, #(PCB_VFPSTATE) 331 bl _C_LABEL(vfp_store) 3321: 333#endif /* VFP */ 334 335 /* r1 now free! */ 336 337 /* Third phase : restore saved context */ 338 339 /* rem: r2 = old PCB */ 340 /* rem: r9 = new PCB */ 341 /* rem: interrupts are enabled */ 342 343 ldr r5, [r9, #(PCB_DACR)] /* r5 = new DACR */ 344 mov r2, #DOMAIN_CLIENT 345 cmp r5, r2, lsl #(PMAP_DOMAIN_KERNEL * 2) /* Sw to kernel thread? */ 346 beq .Lcs_context_switched /* Yup. Don't flush cache */ 347 mrc p15, 0, r0, c3, c0, 0 /* r0 = old DACR */ 348 /* 349 * Get the new L1 table pointer into r11. If we're switching to 350 * an LWP with the same address space as the outgoing one, we can 351 * skip the cache purge and the TTB load. 352 * 353 * To avoid data dep stalls that would happen anyway, we try 354 * and get some useful work done in the mean time. 355 */ 356 mrc p15, 0, r10, c2, c0, 0 /* r10 = old L1 */ 357 ldr r11, [r9, #(PCB_PAGEDIR)] /* r11 = new L1 */ 358 359 360 teq r10, r11 /* Same L1? */ 361 cmpeq r0, r5 /* Same DACR? */ 362 beq .Lcs_context_switched /* yes! */ 363 364#if !defined(CPU_ARM11) && !defined(CPU_CORTEXA) && !defined(CPU_MV_PJ4B) 365 /* 366 * Definately need to flush the cache. 367 */ 368 369 ldr r1, .Lcpufuncs 370 mov lr, pc 371 ldr pc, [r1, #CF_IDCACHE_WBINV_ALL] 372#endif 373.Lcs_cache_purge_skipped: 374 /* rem: r6 = lock */ 375 /* rem: r9 = new PCB */ 376 /* rem: r10 = old L1 */ 377 /* rem: r11 = new L1 */ 378 379 mov r2, #0x00000000 380 ldr r7, [r9, #(PCB_PL1VEC)] 381 382 /* 383 * Ensure the vector table is accessible by fixing up the L1 384 */ 385 cmp r7, #0 /* No need to fixup vector table? */ 386 ldrne r2, [r7] /* But if yes, fetch current value */ 387 ldrne r0, [r9, #(PCB_L1VEC)] /* Fetch new vector_page value */ 388 mcr p15, 0, r5, c3, c0, 0 /* Update DACR for new context */ 389 cmpne r2, r0 /* Stuffing the same value? */ 390#ifndef PMAP_INCLUDE_PTE_SYNC 391 strne r0, [r7] /* Nope, update it */ 392#else 393 beq .Lcs_same_vector 394 str r0, [r7] /* Otherwise, update it */ 395 396 /* 397 * Need to sync the cache to make sure that last store is 398 * visible to the MMU. 399 */ 400 ldr r2, .Lcpufuncs 401 mov r0, r7 402 mov r1, #4 403 mov lr, pc 404 ldr pc, [r2, #CF_DCACHE_WB_RANGE] 405 406.Lcs_same_vector: 407#endif /* PMAP_INCLUDE_PTE_SYNC */ 408 409 cmp r10, r11 /* Switching to the same L1? */ 410 ldr r10, .Lcpufuncs 411 beq .Lcs_same_l1 /* Yup. */ 412 /* 413 * Do a full context switch, including full TLB flush. 414 */ 415 mov r0, r11 416 mov lr, pc 417 ldr pc, [r10, #CF_CONTEXT_SWITCH] 418 419 b .Lcs_context_switched 420 421 /* 422 * We're switching to a different process in the same L1. 423 * In this situation, we only need to flush the TLB for the 424 * vector_page mapping, and even then only if r7 is non-NULL. 425 */ 426.Lcs_same_l1: 427 cmp r7, #0 428 movne r0, #0 /* We *know* vector_page's VA is 0x0 */ 429 movne lr, pc 430 ldrne pc, [r10, #CF_TLB_FLUSHID_SE] 431 /* 432 * We can do that, since 433 * PSR_SVC32_MODE|PSR_UND32_MODE == MSR_UND32_MODE 434 */ 435 436.Lcs_context_switched: 437 438 /* Release the old thread */ 439 str r6, [r4, #TD_LOCK] 440#if defined(SCHED_ULE) && defined(SMP) 441 ldr r6, .Lblocked_lock 442 GET_CURTHREAD_PTR(r3) 443 4441: 445 ldr r4, [r3, #TD_LOCK] 446 cmp r4, r6 447 beq 1b 448#endif 449 450 /* XXXSCW: Safe to re-enable FIQs here */ 451 452 /* rem: r9 = new PCB */ 453 454 mrs r3, cpsr 455 /* 456 * We can do that, since 457 * PSR_SVC32_MODE|PSR_UND32_MODE == MSR_UND32_MODE 458 */ 459 orr r2, r3, #(PSR_UND32_MODE) 460 msr cpsr_c, r2 461 462 ldr sp, [r9, #(PCB_UND_SP)] 463 464 msr cpsr_c, r3 /* Restore the old mode */ 465 /* Restore all the save registers */ 466#ifndef _ARM_ARCH_5E 467 add r7, r9, #PCB_R8 468 ldmia r7, {r8-r13} 469 sub r7, r7, #PCB_R8 /* restore PCB pointer */ 470#else 471 mov r7, r9 472 ldr r8, [r7, #(PCB_R8)] 473 ldr r9, [r7, #(PCB_R9)] 474 ldr r10, [r7, #(PCB_R10)] 475 ldr r11, [r7, #(PCB_R11)] 476 ldr r12, [r7, #(PCB_R12)] 477 ldr r13, [r7, #(PCB_SP)] 478#endif 479 480 /* rem: r5 = new lwp's proc */ 481 /* rem: r6 = lock */ 482 /* rem: r7 = new PCB */ 483 484.Lswitch_return: 485 486 /* 487 * Pull the registers that got pushed when either savectx() or 488 * cpu_switch() was called and return. 489 */ 490 add sp, sp, #4; 491 ldmfd sp!, {r4-r7, pc} 492#ifdef DIAGNOSTIC 493.Lswitch_bogons: 494 adr r0, .Lswitch_panic_str 495 bl _C_LABEL(panic) 4961: nop 497 b 1b 498 499.Lswitch_panic_str: 500 .asciz "cpu_switch: sched_qs empty with non-zero sched_whichqs!\n" 501#endif 502END(cpu_switch) 503 504ENTRY(savectx) 505 stmfd sp!, {r4-r7, lr} 506 sub sp, sp, #4 507 /* 508 * r0 = pcb 509 */ 510 /* Store all the registers in the process's pcb */ 511 add r2, r0, #(PCB_R8) 512 stmia r2, {r8-r13} 513#ifdef VFP 514 /* 515 * vfp_store will clear pcpu->pc_vfpcthread, save 516 * registers and state, and modify the control as needed. 517 * a future exception will bounce the backup settings in the fp unit. 518 */ 519 GET_PCPU(r7) 520 ldr r4, [r7, #(PC_VFPCTHREAD)] /* vfp thread */ 521 ldr r2, [r7, #(PC_CURTHREAD)] /* current thread */ 522 cmp r4, r2 523 bne 1f 524#ifdef SMP 525 ldr r2, [r7, #(PC_CPU)] /* last used on this cpu? */ 526 ldr r3, [r0, #(PCB_VFPCPU)] 527 cmp r2, r3 528 bne 1f /* no. these values are stale */ 529#endif 530 add r0, r0, #(PCB_VFPSTATE) 531 bl _C_LABEL(vfp_store) 5321: 533#endif /* VFP */ 534 add sp, sp, #4; 535 ldmfd sp!, {r4-r7, pc} 536END(savectx) 537 538ENTRY(fork_trampoline) 539 STOP_UNWINDING /* Can't unwind beyond the thread enty point */ 540 mov r1, r5 541 mov r2, sp 542 mov r0, r4 543 mov fp, #0 544 bl _C_LABEL(fork_exit) 545 /* Kill irq"s */ 546 mrs r0, cpsr 547 orr r0, r0, #(I32_bit|F32_bit) 548 msr cpsr_c, r0 549 DO_AST 550 PULLFRAME 551 552 movs pc, lr /* Exit */ 553 554AST_LOCALS 555END(fork_trampoline) 556 557