swtch-v4.S revision 138414
1129198Scognet/* $NetBSD: cpuswitch.S,v 1.41 2003/11/15 08:44:18 scw Exp $ */ 2129198Scognet 3129198Scognet/* 4129198Scognet * Copyright 2003 Wasabi Systems, Inc. 5129198Scognet * All rights reserved. 6129198Scognet * 7129198Scognet * Written by Steve C. Woodford for Wasabi Systems, Inc. 8129198Scognet * 9129198Scognet * Redistribution and use in source and binary forms, with or without 10129198Scognet * modification, are permitted provided that the following conditions 11129198Scognet * are met: 12129198Scognet * 1. Redistributions of source code must retain the above copyright 13129198Scognet * notice, this list of conditions and the following disclaimer. 14129198Scognet * 2. Redistributions in binary form must reproduce the above copyright 15129198Scognet * notice, this list of conditions and the following disclaimer in the 16129198Scognet * documentation and/or other materials provided with the distribution. 17129198Scognet * 3. All advertising materials mentioning features or use of this software 18129198Scognet * must display the following acknowledgement: 19129198Scognet * This product includes software developed for the NetBSD Project by 20129198Scognet * Wasabi Systems, Inc. 21129198Scognet * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22129198Scognet * or promote products derived from this software without specific prior 23129198Scognet * written permission. 24129198Scognet * 25129198Scognet * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26129198Scognet * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27129198Scognet * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28129198Scognet * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29129198Scognet * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30129198Scognet * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31129198Scognet * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32129198Scognet * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33129198Scognet * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34129198Scognet * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35129198Scognet * POSSIBILITY OF SUCH DAMAGE. 36129198Scognet */ 37129198Scognet/* 38129198Scognet * Copyright (c) 1994-1998 Mark Brinicombe. 39129198Scognet * Copyright (c) 1994 Brini. 40129198Scognet * All rights reserved. 41129198Scognet * 42129198Scognet * This code is derived from software written for Brini by Mark Brinicombe 43129198Scognet * 44129198Scognet * Redistribution and use in source and binary forms, with or without 45129198Scognet * modification, are permitted provided that the following conditions 46129198Scognet * are met: 47129198Scognet * 1. Redistributions of source code must retain the above copyright 48129198Scognet * notice, this list of conditions and the following disclaimer. 49129198Scognet * 2. Redistributions in binary form must reproduce the above copyright 50129198Scognet * notice, this list of conditions and the following disclaimer in the 51129198Scognet * documentation and/or other materials provided with the distribution. 52129198Scognet * 3. All advertising materials mentioning features or use of this software 53129198Scognet * must display the following acknowledgement: 54129198Scognet * This product includes software developed by Brini. 55129198Scognet * 4. The name of the company nor the name of the author may be used to 56129198Scognet * endorse or promote products derived from this software without specific 57129198Scognet * prior written permission. 58129198Scognet * 59129198Scognet * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED 60129198Scognet * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 61129198Scognet * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 62129198Scognet * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 63129198Scognet * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 64129198Scognet * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 65129198Scognet * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 66129198Scognet * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 67129198Scognet * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 68129198Scognet * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 69129198Scognet * SUCH DAMAGE. 70129198Scognet * 71129198Scognet * RiscBSD kernel project 72129198Scognet * 73129198Scognet * cpuswitch.S 74129198Scognet * 75129198Scognet * cpu switching functions 76129198Scognet * 77129198Scognet * Created : 15/10/94 78129198Scognet * 79129198Scognet */ 80129198Scognet 81137274Scognet#include "assym.s" 82137274Scognet 83129198Scognet#include <machine/asm.h> 84129198Scognet#include <machine/asmacros.h> 85129198Scognet#include <machine/armreg.h> 86129198Scognet__FBSDID("$FreeBSD: head/sys/arm/arm/swtch.S 138414 2004-12-05 22:46:59Z cognet $"); 87129198Scognet 88129198Scognet 89129198Scognet/* 90129198Scognet * New experimental definitions of IRQdisable and IRQenable 91129198Scognet * These keep FIQ's enabled since FIQ's are special. 92129198Scognet */ 93129198Scognet 94129198Scognet#define DOMAIN_CLIENT 0x01 95129198Scognet#define IRQdisable \ 96129198Scognet mrs r14, cpsr ; \ 97129198Scognet orr r14, r14, #(I32_bit) ; \ 98129198Scognet msr cpsr_c, r14 ; \ 99129198Scognet 100129198Scognet#define IRQenable \ 101129198Scognet mrs r14, cpsr ; \ 102129198Scognet bic r14, r14, #(I32_bit) ; \ 103129198Scognet msr cpsr_c, r14 ; \ 104129198Scognet 105129198Scognet/* 106129198Scognet * These are used for switching the translation table/DACR. 107129198Scognet * Since the vector page can be invalid for a short time, we must 108129198Scognet * disable both regular IRQs *and* FIQs. 109129198Scognet * 110129198Scognet * XXX: This is not necessary if the vector table is relocated. 111129198Scognet */ 112129198Scognet#define IRQdisableALL \ 113129198Scognet mrs r14, cpsr ; \ 114129198Scognet orr r14, r14, #(I32_bit | F32_bit) ; \ 115129198Scognet msr cpsr_c, r14 116129198Scognet 117129198Scognet#define IRQenableALL \ 118129198Scognet mrs r14, cpsr ; \ 119129198Scognet bic r14, r14, #(I32_bit | F32_bit) ; \ 120129198Scognet msr cpsr_c, r14 121129198Scognet 122129198Scognet.Lcurpcb: 123129198Scognet .word _C_LABEL(__pcpu) + PC_CURPCB 124129198Scognet.Lcpufuncs: 125129198Scognet .word _C_LABEL(cpufuncs) 126129198Scognet.Lblock_userspace_access: 127129198Scognet .word _C_LABEL(block_userspace_access) 128129198Scognet.Lcpu_do_powersave: 129129198Scognet .word _C_LABEL(cpu_do_powersave) 130135655ScognetENTRY(cpu_throw) 131135655Scognet mov r4, r0 132135655Scognet mov r5, r1 133129198Scognet 134135655Scognet /* 135137274Scognet * r4 = oldtd 136137274Scognet * r5 = newtd 137135655Scognet */ 138129198Scognet 139137274Scognet ldr r7, [r5, #(TD_PCB)] /* r7 = new thread's PCB */ 140135655Scognet 141135655Scognet /* Switch to lwp0 context */ 142135655Scognet 143135655Scognet ldr r9, .Lcpufuncs 144135655Scognet mov lr, pc 145135655Scognet ldr pc, [r9, #CF_IDCACHE_WBINV_ALL] 146135655Scognet ldr r0, [r7, #(PCB_PL1VEC)] 147135655Scognet ldr r1, [r7, #(PCB_DACR)] 148135655Scognet /* 149135655Scognet * r0 = Pointer to L1 slot for vector_page (or NULL) 150135655Scognet * r1 = lwp0's DACR 151135655Scognet * r4 = lwp we're switching from 152135655Scognet * r5 = lwp0 153135655Scognet * r6 = exit func 154135655Scognet * r7 = lwp0's PCB 155135655Scognet * r9 = cpufuncs 156135655Scognet */ 157135655Scognet 158135655Scognet /* 159135655Scognet * Ensure the vector table is accessible by fixing up lwp0's L1 160135655Scognet */ 161135655Scognet cmp r0, #0 /* No need to fixup vector table? */ 162135655Scognet ldrne r3, [r0] /* But if yes, fetch current value */ 163135655Scognet ldrne r2, [r7, #(PCB_L1VEC)] /* Fetch new vector_page value */ 164135655Scognet mcr p15, 0, r1, c3, c0, 0 /* Update DACR for lwp0's context */ 165135655Scognet cmpne r3, r2 /* Stuffing the same value? */ 166135655Scognet strne r2, [r0] /* Store if not. */ 167135655Scognet 168135655Scognet#ifdef PMAP_INCLUDE_PTE_SYNC 169135655Scognet /* 170135655Scognet * Need to sync the cache to make sure that last store is 171135655Scognet * visible to the MMU. 172135655Scognet */ 173135655Scognet movne r1, #4 174135655Scognet movne lr, pc 175135655Scognet ldrne pc, [r9, #CF_DCACHE_WB_RANGE] 176135655Scognet#endif /* PMAP_INCLUDE_PTE_SYNC */ 177135655Scognet 178135655Scognet /* 179135655Scognet * Note: We don't do the same optimisation as cpu_switch() with 180135655Scognet * respect to avoiding flushing the TLB if we're switching to 181135655Scognet * the same L1 since this process' VM space may be about to go 182135655Scognet * away, so we don't want *any* turds left in the TLB. 183135655Scognet */ 184135655Scognet 185135655Scognet /* Switch the memory to the new process */ 186135655Scognet ldr r0, [r7, #(PCB_PAGEDIR)] 187135655Scognet mov lr, pc 188135655Scognet ldr pc, [r9, #CF_CONTEXT_SWITCH] 189135655Scognet 190135655Scognet /* Restore all the save registers */ 191135655Scognet#ifndef __XSCALE__ 192135655Scognet add r1, r7, #PCB_R8 193135655Scognet ldmia r1, {r8-r13} 194135655Scognet#else 195135655Scognet ldr r8, [r7, #(PCB_R8)] 196135655Scognet ldr r9, [r7, #(PCB_R9)] 197135655Scognet ldr r10, [r7, #(PCB_R10)] 198135655Scognet ldr r11, [r7, #(PCB_R11)] 199135655Scognet ldr r12, [r7, #(PCB_R12)] 200135655Scognet ldr r13, [r7, #(PCB_SP)] 201135655Scognet#endif 202135655Scognet 203137274Scognet mov r0, #0x00000000 /* r5 = old lwp = NULL */ 204137274Scognet mov r1, r5 205135655Scognet b .Lswitch_resume 206135655Scognet 207129198ScognetENTRY(cpu_switch) 208129198Scognet stmfd sp!, {r4-r7, lr} 209129198Scognet 210135655Scognet.Lswitch_resume: 211137274Scognet /* rem: r0 = old lwp */ 212129198Scognet /* rem: interrupts are disabled */ 213129198Scognet 214129198Scognet#ifdef MULTIPROCESSOR 215129198Scognet /* XXX use curcpu() */ 216137274Scognet ldr r2, .Lcpu_info_store 217137274Scognet str r2, [r6, #(L_CPU)] 218129198Scognet#endif 219129198Scognet 220129198Scognet /* Process is now on a processor. */ 221129198Scognet 222135655Scognet /* We have a new curthread now so make a note it */ 223129198Scognet ldr r7, .Lcurthread 224137274Scognet str r1, [r7] 225129198Scognet 226129198Scognet /* Hook in a new pcb */ 227129198Scognet ldr r7, .Lcurpcb 228137274Scognet ldr r2, [r1, #TD_PCB] 229137274Scognet str r2, [r7] 230129198Scognet 231129198Scognet 232129198Scognet /* 233129198Scognet * If the old lwp on entry to cpu_switch was zero then the 234129198Scognet * process that called it was exiting. This means that we do 235129198Scognet * not need to save the current context. Instead we can jump 236129198Scognet * straight to restoring the context for the new process. 237129198Scognet */ 238129198Scognet teq r0, #0x00000000 239137274Scognet beq .Lswitch_return 240129198Scognet 241137274Scognet /* rem: r1 = new process */ 242129198Scognet /* rem: interrupts are enabled */ 243129198Scognet 244129198Scognet /* Stage two : Save old context */ 245129198Scognet 246129198Scognet /* Get the user structure for the old lwp. */ 247137274Scognet ldr r2, [r0, #(TD_PCB)] 248129198Scognet 249129198Scognet /* Save all the registers in the old lwp's pcb */ 250129198Scognet#ifndef __XSCALE__ 251137274Scognet add r7, r2, #(PCB_R8) 252129198Scognet stmia r7, {r8-r13} 253129198Scognet#else 254137274Scognet strd r8, [r2, #(PCB_R8)] 255137274Scognet strd r10, [r2, #(PCB_R10)] 256137274Scognet strd r12, [r2, #(PCB_R12)] 257129198Scognet#endif 258135655Scognet 259129198Scognet /* 260129198Scognet * NOTE: We can now use r8-r13 until it is time to restore 261129198Scognet * them for the new process. 262129198Scognet */ 263129198Scognet 264129198Scognet 265129198Scognet /* Get the user structure for the new process in r9 */ 266137274Scognet ldr r9, [r1, #(TD_PCB)] 267129198Scognet 268137274Scognet /* r1 now free! */ 269137274Scognet 270129198Scognet /* 271129198Scognet * This can be optimised... We know we want to go from SVC32 272129198Scognet * mode to UND32 mode 273129198Scognet */ 274129198Scognet mrs r3, cpsr 275129198Scognet bic r2, r3, #(PSR_MODE) 276135655Scognet orr r2, r2, #(PSR_UND32_MODE) 277129198Scognet msr cpsr_c, r2 278129198Scognet 279129198Scognet str sp, [r8, #(PCB_UND_SP)] 280129198Scognet 281129198Scognet msr cpsr_c, r3 /* Restore the old mode */ 282129198Scognet /* rem: r8 = old PCB */ 283129198Scognet /* rem: r9 = new PCB */ 284129198Scognet /* rem: interrupts are enabled */ 285129198Scognet 286129198Scognet /* What else needs to be saved Only FPA stuff when that is supported */ 287129198Scognet 288129198Scognet /* Third phase : restore saved context */ 289129198Scognet 290129198Scognet /* rem: r8 = old PCB */ 291129198Scognet /* rem: r9 = new PCB */ 292129198Scognet /* rem: interrupts are enabled */ 293129198Scognet 294138414Scognet ldr r5, [r9, #(PCB_DACR)] /* r5 = new DACR */ 295138414Scognet mov r2, #DOMAIN_CLIENT 296138414Scognet cmp r5, r2, lsl #(PMAP_DOMAIN_KERNEL * 2) /* Sw to kernel thread? */ 297138414Scognet beq .Lcs_context_switched /* Yup. Don't flush cache */ 298138414Scognet mrc p15, 0, r0, c3, c0, 0 /* r0 = old DACR */ 299129198Scognet /* 300129198Scognet * Get the new L1 table pointer into r11. If we're switching to 301129198Scognet * an LWP with the same address space as the outgoing one, we can 302129198Scognet * skip the cache purge and the TTB load. 303129198Scognet * 304129198Scognet * To avoid data dep stalls that would happen anyway, we try 305129198Scognet * and get some useful work done in the mean time. 306129198Scognet */ 307138414Scognet mrc p15, 0, r10, c2, c0, 0 /* r10 = old L1 */ 308129198Scognet ldr r11, [r9, #(PCB_PAGEDIR)] /* r11 = new L1 */ 309129198Scognet 310129198Scognet 311129198Scognet teq r10, r11 /* Same L1? */ 312137274Scognet cmpeq r0, r5 /* Same DACR? */ 313129198Scognet beq .Lcs_context_switched /* yes! */ 314129198Scognet 315129198Scognet /* 316129198Scognet * Definately need to flush the cache. 317129198Scognet */ 318129198Scognet 319129198Scognet ldr r1, .Lcpufuncs 320129198Scognet mov lr, pc 321129198Scognet ldr pc, [r1, #CF_IDCACHE_WBINV_ALL] 322129198Scognet.Lcs_cache_purge_skipped: 323137274Scognet /* rem: r4 = &block_userspace_access */ 324129198Scognet /* rem: r6 = new lwp */ 325129198Scognet /* rem: r9 = new PCB */ 326129198Scognet /* rem: r10 = old L1 */ 327129198Scognet /* rem: r11 = new L1 */ 328129198Scognet 329129198Scognet mov r2, #0x00000000 330129198Scognet ldr r7, [r9, #(PCB_PL1VEC)] 331129198Scognet 332129198Scognet /* 333129198Scognet * Ensure the vector table is accessible by fixing up the L1 334129198Scognet */ 335129198Scognet cmp r7, #0 /* No need to fixup vector table? */ 336129198Scognet ldrne r2, [r7] /* But if yes, fetch current value */ 337129198Scognet ldrne r0, [r9, #(PCB_L1VEC)] /* Fetch new vector_page value */ 338137274Scognet mcr p15, 0, r5, c3, c0, 0 /* Update DACR for new context */ 339129198Scognet cmpne r2, r0 /* Stuffing the same value? */ 340135655Scognet#ifndef PMAP_INCLUDE_PTE_SYNC 341129198Scognet strne r0, [r7] /* Nope, update it */ 342129198Scognet#else 343129198Scognet beq .Lcs_same_vector 344129198Scognet str r0, [r7] /* Otherwise, update it */ 345129198Scognet 346129198Scognet /* 347129198Scognet * Need to sync the cache to make sure that last store is 348129198Scognet * visible to the MMU. 349129198Scognet */ 350129198Scognet ldr r2, .Lcpufuncs 351129198Scognet mov r0, r7 352129198Scognet mov r1, #4 353129198Scognet mov lr, pc 354129198Scognet ldr pc, [r2, #CF_DCACHE_WB_RANGE] 355129198Scognet 356129198Scognet.Lcs_same_vector: 357129198Scognet#endif /* PMAP_INCLUDE_PTE_SYNC */ 358129198Scognet 359129198Scognet cmp r10, r11 /* Switching to the same L1? */ 360129198Scognet ldr r10, .Lcpufuncs 361129198Scognet beq .Lcs_same_l1 /* Yup. */ 362129198Scognet /* 363129198Scognet * Do a full context switch, including full TLB flush. 364129198Scognet */ 365129198Scognet mov r0, r11 366129198Scognet mov lr, pc 367129198Scognet ldr pc, [r10, #CF_CONTEXT_SWITCH] 368129198Scognet 369129198Scognet b .Lcs_context_switched 370129198Scognet 371129198Scognet /* 372129198Scognet * We're switching to a different process in the same L1. 373129198Scognet * In this situation, we only need to flush the TLB for the 374129198Scognet * vector_page mapping, and even then only if r7 is non-NULL. 375129198Scognet */ 376129198Scognet.Lcs_same_l1: 377129198Scognet cmp r7, #0 378129198Scognet movne r0, #0 /* We *know* vector_page's VA is 0x0 */ 379129198Scognet movne lr, pc 380129198Scognet ldrne pc, [r10, #CF_TLB_FLUSHID_SE] 381129198Scognet 382129198Scognet.Lcs_context_switched: 383129198Scognet 384129198Scognet /* XXXSCW: Safe to re-enable FIQs here */ 385129198Scognet 386129198Scognet /* rem: r9 = new PCB */ 387129198Scognet 388129198Scognet /* 389129198Scognet * This can be optimised... We know we want to go from SVC32 390129198Scognet * mode to UND32 mode 391129198Scognet */ 392129198Scognet mrs r3, cpsr 393129198Scognet bic r2, r3, #(PSR_MODE) 394129198Scognet orr r2, r2, #(PSR_UND32_MODE) 395129198Scognet msr cpsr_c, r2 396129198Scognet 397129198Scognet ldr sp, [r9, #(PCB_UND_SP)] 398129198Scognet 399129198Scognet msr cpsr_c, r3 /* Restore the old mode */ 400129198Scognet /* Restore all the save registers */ 401129198Scognet#ifndef __XSCALE__ 402129198Scognet add r7, r9, #PCB_R8 403129198Scognet ldmia r7, {r8-r13} 404129198Scognet sub r7, r7, #PCB_R8 /* restore PCB pointer */ 405129198Scognet#else 406129198Scognet mov r7, r9 407129198Scognet ldr r8, [r7, #(PCB_R8)] 408129198Scognet ldr r9, [r7, #(PCB_R9)] 409129198Scognet ldr r10, [r7, #(PCB_R10)] 410129198Scognet ldr r11, [r7, #(PCB_R11)] 411129198Scognet ldr r12, [r7, #(PCB_R12)] 412129198Scognet ldr r13, [r7, #(PCB_SP)] 413129198Scognet#endif 414129198Scognet 415129198Scognet /* rem: r6 = new lwp */ 416129198Scognet /* rem: r7 = new pcb */ 417129198Scognet 418129198Scognet#ifdef ARMFPE 419129198Scognet add r0, r7, #(USER_SIZE) & 0x00ff 420129198Scognet add r0, r0, #(USER_SIZE) & 0xff00 421129198Scognet bl _C_LABEL(arm_fpe_core_changecontext) 422129198Scognet#endif 423129198Scognet 424129198Scognet /* rem: r5 = new lwp's proc */ 425129198Scognet /* rem: r6 = new lwp */ 426129198Scognet /* rem: r7 = new PCB */ 427129198Scognet 428129198Scognet.Lswitch_return: 429129198Scognet 430129198Scognet /* 431129198Scognet * Pull the registers that got pushed when either savectx() or 432129198Scognet * cpu_switch() was called and return. 433129198Scognet */ 434129198Scognet ldmfd sp!, {r4-r7, pc} 435129198Scognet#ifdef DIAGNOSTIC 436129198Scognet.Lswitch_bogons: 437129198Scognet adr r0, .Lswitch_panic_str 438129198Scognet bl _C_LABEL(panic) 439129198Scognet1: nop 440129198Scognet b 1b 441129198Scognet 442129198Scognet.Lswitch_panic_str: 443129198Scognet .asciz "cpu_switch: sched_qs empty with non-zero sched_whichqs!\n" 444129198Scognet#endif 445129198ScognetENTRY(savectx) 446137463Scognet RET 447129198ScognetENTRY(fork_trampoline) 448129198Scognet mov r1, r5 449129198Scognet mov r2, sp 450129198Scognet mov r0, r4 451137976Scognet mov fp, #0 452129198Scognet bl _C_LABEL(fork_exit) 453135655Scognet /* Kill irq"s */ 454135655Scognet mrs r0, cpsr 455135655Scognet orr r0, r0, #(I32_bit) 456135655Scognet msr cpsr_c, r0 457129198Scognet PULLFRAME 458129198Scognet 459129198Scognet movs pc, lr /* Exit */ 460129198Scognet 461135655ScognetAST_LOCALS 462