1/* $NetBSD: cpuswitch.S,v 1.62 2011/02/01 01:20:14 matt 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#include "opt_armfpe.h" 81#include "opt_arm32_pmap.h" 82#include "opt_multiprocessor.h" 83#include "opt_cpuoptions.h" 84#include "opt_lockdebug.h" 85 86#include "assym.h" 87#include <machine/param.h> 88#include <machine/frame.h> 89#include <machine/asm.h> 90#include <machine/cpu.h> 91 92 RCSID("$NetBSD: cpuswitch.S,v 1.62 2011/02/01 01:20:14 matt Exp $") 93 94/* LINTSTUB: include <sys/param.h> */ 95 96#undef IRQdisable 97#undef IRQenable 98 99/* 100 * New experimental definitions of IRQdisable and IRQenable 101 * These keep FIQ's enabled since FIQ's are special. 102 */ 103 104#ifdef _ARM_ARCH_6 105#define IRQdisable cpsid i 106#define IRQenable cpsie i 107#else 108#define IRQdisable \ 109 mrs r14, cpsr ; \ 110 orr r14, r14, #(I32_bit) ; \ 111 msr cpsr_c, r14 112 113#define IRQenable \ 114 mrs r14, cpsr ; \ 115 bic r14, r14, #(I32_bit) ; \ 116 msr cpsr_c, r14 117 118#endif 119 120 .text 121.Lpmap_previous_active_lwp: 122 .word _C_LABEL(pmap_previous_active_lwp) 123 124/* 125 * struct lwp * 126 * cpu_switchto(struct lwp *current, struct lwp *next) 127 * 128 * Switch to the specified next LWP 129 * Arguments: 130 * 131 * r0 'struct lwp *' of the current LWP (or NULL if exiting) 132 * r1 'struct lwp *' of the LWP to switch to 133 * r2 returning 134 */ 135ENTRY(cpu_switchto) 136 mov ip, sp 137 stmfd sp!, {r4-r7, ip, lr} 138 139 /* move lwps into caller saved registers */ 140 mov r6, r1 141 mov r4, r0 142 143#ifdef PROCESS_ID_CURCPU 144 GET_CURCPU(r7) 145#elif defined(PROCESS_ID_IS_CURLWP) 146 mcr p15, 0, r0, c13, c0, 4 /* get old lwp (r4 maybe 0) */ 147 ldr r7, [r0, #(L_CPU)] /* get cpu from old lwp */ 148#elif !defined(MULTIPROCESSOR) 149 ldr r7, [r6, #L_CPU] /* get cpu from new lwp */ 150#else 151#error curcpu() method not defined 152#endif 153 154 /* rem: r4 = old lwp */ 155 /* rem: r6 = new lwp */ 156 /* rem: r7 = curcpu() */ 157 158#ifndef __HAVE_UNNESTED_INTRS 159 IRQdisable 160#endif 161 162#ifdef MULTIPROCESSOR 163 str r7, [r6, #(L_CPU)] 164#else 165 /* l->l_cpu initialized in fork1() for single-processor */ 166#endif 167 168#if defined(PROCESS_ID_IS_CURLWP) 169 mcr p15, 0, r6, c13, c0, 4 /* set current lwp */ 170#endif 171#if !defined(PROCESS_ID_IS_CURLWP) || defined(MULTIPROCESSOR) 172 /* We have a new curlwp now so make a note it */ 173 str r6, [r7, #(CI_CURLWP)] 174#endif 175 176 /* Hook in a new pcb */ 177 ldr r0, [r6, #(L_PCB)] 178 str r0, [r7, #(CI_CURPCB)] 179 mov r7, r0 180 181 /* At this point we can allow IRQ's again. */ 182#ifndef __HAVE_UNNESTED_INTRS 183 IRQenable 184#endif 185 186 /* rem: r4 = old lwp */ 187 /* rem: r6 = new lwp */ 188 /* rem: r7 = new pcb */ 189 /* rem: interrupts are enabled */ 190 191 /* 192 * If the old lwp on entry to cpu_switchto was zero then the 193 * process that called it was exiting. This means that we do 194 * not need to save the current context. Instead we can jump 195 * straight to restoring the context for the new process. 196 */ 197 teq r4, #0 198 beq .Ldo_switch 199 200 /* rem: r4 = old lwp */ 201 /* rem: r6 = new lwp */ 202 /* rem: r7 = new pcb */ 203 /* rem: interrupts are enabled */ 204 205 /* Save old context */ 206 207 /* Get the user structure for the old lwp. */ 208 ldr r5, [r4, #(L_PCB)] 209 210 /* Save all the registers in the old lwp's pcb */ 211#if defined(__XSCALE__) || defined(_ARM_ARCH_6) 212 strd r8, [r5, #(PCB_R8)] 213 strd r10, [r5, #(PCB_R10)] 214 strd r12, [r5, #(PCB_R12)] 215#else 216 add r0, r5, #(PCB_R8) 217 stmia r0, {r8-r13} 218#endif 219 220#ifdef _ARM_ARCH_6 221 /* 222 * Save user read/write thread/process id register 223 */ 224 mrc p15, 0, r0, c13, c0, 2 225 str r0, [r5, #(PCB_USER_PID_RW)] 226#endif 227 /* 228 * NOTE: We can now use r8-r13 until it is time to restore 229 * them for the new process. 230 */ 231 232 /* rem: r4 = old lwp */ 233 /* rem: r5 = old pcb */ 234 /* rem: r6 = new lwp */ 235 /* rem: r7 = new pcb */ 236 /* rem: interrupts are enabled */ 237 238#ifdef FPU_VFP 239 /* 240 * Now's a good time to 'save' the VFP context. Note that we 241 * don't really force a save here, which can save time if we 242 * end up restarting the same context. 243 */ 244 bl _C_LABEL(vfp_savecontext) 245#endif 246 247 /* Restore saved context */ 248 249.Ldo_switch: 250 /* rem: r4 = old lwp */ 251 /* rem: r6 = new lwp */ 252 /* rem: r7 = new pcb */ 253 /* rem: interrupts are enabled */ 254 255#ifdef _ARM_ARCH_6 256 /* 257 * Restore user thread/process id registers 258 */ 259 ldr r0, [r7, #(PCB_USER_PID_RW)] 260 mcr p15, 0, r0, c13, c0, 2 261 ldr r0, [r6, #(L_PRIVATE)] 262 mcr p15, 0, r0, c13, c0, 3 263#endif 264 265 ldr r5, [r6, #(L_PROC)] /* fetch the proc for below */ 266 267 /* Restore all the saved registers */ 268#ifdef __XSCALE__ 269 ldr r8, [r7, #(PCB_R8)] 270 ldr r9, [r7, #(PCB_R9)] 271 ldr r10, [r7, #(PCB_R10)] 272 ldr r11, [r7, #(PCB_R11)] 273 ldr r12, [r7, #(PCB_R12)] 274 ldr r13, [r7, #(PCB_SP)] 275#elif defined(_ARM_ARCH_6) 276 ldrd r8, [r7, #(PCB_R8)] 277 ldrd r10, [r7, #(PCB_R10)] 278 ldrd r12, [r7, #(PCB_R12)] 279#else 280 add r0, r7, #PCB_R8 281 ldmia r0, {r8-r13} 282#endif 283 284 /* Record the old lwp for pmap_activate()'s benefit */ 285 ldr r1, .Lpmap_previous_active_lwp 286 str r4, [r1] 287 288 /* rem: r4 = old lwp */ 289 /* rem: r5 = new lwp's proc */ 290 /* rem: r6 = new lwp */ 291 /* rem: r7 = new pcb */ 292 293#ifdef FPU_VFP 294 mov r0, r6 295 bl _C_LABEL(vfp_loadcontext) 296#endif 297#ifdef ARMFPE 298 add r0, r7, #(PCB_SIZE) & 0x00ff 299 add r0, r0, #(PCB_SIZE) & 0xff00 300 bl _C_LABEL(arm_fpe_core_changecontext) 301#endif 302 303 /* rem: r4 = old lwp */ 304 /* rem: r5 = new lwp's proc */ 305 /* rem: r6 = new lwp */ 306 /* rem: r7 = new PCB */ 307 308 /* 309 * Check for restartable atomic sequences (RAS). 310 */ 311 312 ldr r2, [r5, #(P_RASLIST)] 313 ldr r1, [r7, #(PCB_TF)] /* r1 = trapframe (used below) */ 314 teq r2, #0 /* p->p_nras == 0? */ 315 bne .Lswitch_do_ras /* no, check for one */ 316 317.Lswitch_return: 318 /* cpu_switchto returns the old lwp */ 319 mov r0, r4 320 /* lwp_trampoline expects new lwp as it's second argument */ 321 mov r1, r6 322 323 /* 324 * Pull the registers that got pushed when cpu_switchto() was called, 325 * and return. 326 */ 327 ldmfd sp, {r4-r7, sp, pc} 328 329.Lswitch_do_ras: 330 ldr r1, [r1, #(TF_PC)] /* second ras_lookup() arg */ 331 mov r0, r5 /* first ras_lookup() arg */ 332 bl _C_LABEL(ras_lookup) 333 cmn r0, #1 /* -1 means "not in a RAS" */ 334 ldrne r1, [r7, #(PCB_TF)] 335 strne r0, [r1, #(TF_PC)] 336 b .Lswitch_return 337 338ENTRY(lwp_trampoline) 339 /* 340 * cpu_switchto gives us: 341 * 342 * arg0(r0) = old lwp 343 * arg1(r1) = new lwp 344 */ 345 bl _C_LABEL(lwp_startup) 346 347 mov r0, r5 348 mov r1, sp 349 mov lr, pc 350 mov pc, r4 351 352 /* Kill irq's */ 353 mrs r0, cpsr 354 orr r0, r0, #(IF32_bits) 355 msr cpsr_c, r0 356 357 PULLFRAME 358 359 movs pc, lr /* Exit */ 360 361#ifdef __HAVE_FAST_SOFTINTS 362/* 363 * Called at IPL_HIGH 364 * r0 = new lwp 365 * r1 = ipl for softint_dispatch 366 */ 367ENTRY_NP(softint_switch) 368 stmfd sp!, {r4, r6, r7, lr} 369 370 ldr r7, [r0, #L_CPU] /* get curcpu */ 371#if defined(PROCESS_ID_IS_CURLWP) 372 mrc p15, 0, r4, c13, c0, 4 /* get old lwp */ 373#else 374 ldr r4, [r7, #(CI_CURLWP)] /* get old lwp */ 375#endif 376 mrs r6, cpsr /* we need to save this */ 377 378 /* 379 * If the soft lwp blocks, it needs to return to softint_tramp 380 */ 381 mov r2, sp /* think ip */ 382 adr r3, softint_tramp /* think lr */ 383 stmfd sp!, {r2-r3} 384 stmfd sp!, {r4-r7} 385 386 mov r5, r0 /* save new lwp */ 387 388 ldr r2, [r4, #(L_PCB)] /* get old lwp's pcb */ 389 390 /* Save all the registers into the old lwp's pcb */ 391#if defined(__XSCALE__) || defined(_ARM_ARCH_6) 392 strd r8, [r2, #(PCB_R8)] 393 strd r10, [r2, #(PCB_R10)] 394 strd r12, [r2, #(PCB_R12)] 395#else 396 add r3, r2, #(PCB_R8) 397 stmia r3, {r8-r13} 398#endif 399 400 /* this is an invariant so load before disabling intrs */ 401 ldr r2, [r5, #(L_PCB)] /* get new lwp's pcb */ 402 403#ifndef __HAVE_UNNESTED_INTRS 404 IRQdisable 405#endif 406 /* 407 * We're switching to a bound LWP so its l_cpu is already correct. 408 */ 409#if defined(PROCESS_ID_IS_CURLWP) 410 mcr p15, 0, r5, c13, c0, 4 /* save new lwp */ 411#endif 412#if !defined(PROCESS_ID_IS_CURLWP) || defined(MULTIPROCESSOR) 413 str r5, [r7, #(CI_CURLWP)] /* save new lwp */ 414#endif 415 416 /* Hook in a new pcb */ 417 str r2, [r7, #(CI_CURPCB)] 418 419 /* 420 * Normally, we'd get {r8-r13} but since this is a softint lwp 421 * it's existing state doesn't matter. We start the stack just 422 * below the trapframe. 423 */ 424 ldr sp, [r2, #(PCB_TF)] /* get new lwp's stack ptr */ 425 426 /* At this point we can allow IRQ's again. */ 427#ifndef __HAVE_UNNESTED_INTRS 428 IRQenable 429#endif 430 431 /* r1 still has ipl */ 432 mov r0, r4 /* r0 has pinned (old) lwp */ 433 bl _C_LABEL(softint_dispatch) 434 /* 435 * If we've returned, we need to change everything back and return. 436 */ 437 ldr r2, [r4, #(L_PCB)] /* get pinned lwp's pcb */ 438 439#ifndef __HAVE_UNNESTED_INTRS 440 IRQdisable 441#endif 442 /* 443 * We don't need to restore all the registers since another lwp was 444 * never executed. But we do need the SP from the formerly pinned lwp. 445 */ 446 447#if defined(PROCESS_ID_IS_CURLWP) 448 mcr p15, 0, r4, c13, c0, 4 /* restore pinned lwp */ 449#endif 450#if !defined(PROCESS_ID_IS_CURLWP) || defined(MULTIPROCESSOR) 451 str r4, [r7, #(CI_CURLWP)] /* restore pinned lwp */ 452#endif 453 str r2, [r7, #(CI_CURPCB)] /* restore the curpcb */ 454 ldr sp, [r2, #(PCB_SP)] /* now running on the old stack. */ 455 456 /* At this point we can allow IRQ's again. */ 457 msr cpsr_c, r6 458 459 /* 460 * Grab the registers that got pushed at the start and return. 461 */ 462 ldmfd sp!, {r4-r7, ip, lr} /* eat switch frame */ 463 ldmfd sp!, {r4, r6, r7, pc} /* pop stack and return */ 464 465END(softint_switch) 466 467/* 468 * r0 = previous LWP (the soft lwp) 469 * r4 = original LWP (the current lwp) 470 * r6 = original CPSR 471 * r7 = curcpu() 472 */ 473ENTRY_NP(softint_tramp) 474 ldr r3, [r7, #(CI_MTX_COUNT)] /* readust after mi_switch */ 475 add r3, r3, #1 476 str r3, [r7, #(CI_MTX_COUNT)] 477 478 mov r3, #0 /* tell softint_dispatch */ 479 str r3, [r0, #(L_CTXSWTCH)] /* the soft lwp blocked */ 480 481 msr cpsr_c, r6 /* restore interrupts */ 482 ldmfd sp!, {r4, r6, r7, pc} /* pop stack and return */ 483END(softint_tramp) 484#endif /* __HAVE_FAST_SOFTINTS */ 485