1/* $NetBSD: cpuswitch.S,v 1.37 2003/10/13 21:44:27 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#include "opt_armfpe.h" 81#include "opt_arm32_pmap.h" 82#include "opt_multiprocessor.h" 83#include "opt_lockdebug.h" 84 85#include "assym.h" 86#include <machine/param.h> 87#include <machine/cpu.h> 88#include <machine/frame.h> 89#include <machine/asm.h> 90 91/* LINTSTUB: include <sys/param.h> */ 92 93#undef IRQdisable 94#undef IRQenable 95 96/* 97 * New experimental definitions of IRQdisable and IRQenable 98 * These keep FIQ's enabled since FIQ's are special. 99 */ 100 101#define IRQdisable \ 102 mrs r14, cpsr ; \ 103 orr r14, r14, #(I32_bit) ; \ 104 msr cpsr_c, r14 ; \ 105 106#define IRQenable \ 107 mrs r14, cpsr ; \ 108 bic r14, r14, #(I32_bit) ; \ 109 msr cpsr_c, r14 ; \ 110 111/* 112 * These are used for switching the translation table/DACR. 113 * Since the vector page can be invalid for a short time, we must 114 * disable both regular IRQs *and* FIQs. 115 * 116 * XXX: This is not necessary if the vector table is relocated. 117 */ 118#define IRQdisableALL \ 119 mrs r14, cpsr ; \ 120 orr r14, r14, #(I32_bit | F32_bit) ; \ 121 msr cpsr_c, r14 122 123#define IRQenableALL \ 124 mrs r14, cpsr ; \ 125 bic r14, r14, #(I32_bit | F32_bit) ; \ 126 msr cpsr_c, r14 127 128 .text 129 130.Lwhichqs: 131 .word _C_LABEL(sched_whichqs) 132 133.Lqs: 134 .word _C_LABEL(sched_qs) 135 136/* 137 * cpuswitch() 138 * 139 * preforms a process context switch. 140 * This function has several entry points 141 */ 142 143#ifdef MULTIPROCESSOR 144.Lcpu_info_store: 145 .word _C_LABEL(cpu_info_store) 146.Lcurlwp: 147 /* FIXME: This is bogus in the general case. */ 148 .word _C_LABEL(cpu_info_store) + CI_CURLWP 149 150.Lcurpcb: 151 .word _C_LABEL(cpu_info_store) + CI_CURPCB 152#else 153.Lcurlwp: 154 .word _C_LABEL(curlwp) 155 156.Lcurpcb: 157 .word _C_LABEL(curpcb) 158#endif 159 160.Lwant_resched: 161 .word _C_LABEL(want_resched) 162 163.Lcpufuncs: 164 .word _C_LABEL(cpufuncs) 165 166#ifndef MULTIPROCESSOR 167 .data 168 .global _C_LABEL(curpcb) 169_C_LABEL(curpcb): 170 .word 0x00000000 171 .text 172#endif 173 174.Lblock_userspace_access: 175 .word _C_LABEL(block_userspace_access) 176 177.Lcpu_do_powersave: 178 .word _C_LABEL(cpu_do_powersave) 179 180.Lpmap_kernel_cstate: 181 .word (kernel_pmap_store + PMAP_CSTATE) 182 183.Llast_cache_state_ptr: 184 .word _C_LABEL(pmap_cache_state) 185 186/* 187 * Idle loop, exercised while waiting for a process to wake up. 188 * 189 * NOTE: When we jump back to .Lswitch_search, we must have a 190 * pointer to whichqs in r7, which is what it is when we arrive 191 * here. 192 */ 193/* LINTSTUB: Ignore */ 194ASENTRY_NP(idle) 195#if defined(MULTIPROCESSOR) || defined(LOCKDEBUG) 196 bl _C_LABEL(sched_unlock_idle) 197#endif 198 ldr r3, .Lcpu_do_powersave 199 200 /* Enable interrupts */ 201 IRQenable 202 203 /* If we don't want to sleep, use a simpler loop. */ 204 ldr r3, [r3] /* r3 = cpu_do_powersave */ 205 teq r3, #0 206 bne 2f 207 208 /* Non-powersave idle. */ 2091: /* should maybe do uvm pageidlezero stuff here */ 210 ldr r3, [r7] /* r3 = whichqs */ 211 teq r3, #0x00000000 212 bne .Lswitch_search 213 b 1b 214 2152: /* Powersave idle. */ 216 ldr r4, .Lcpufuncs 2173: ldr r3, [r7] /* r3 = whichqs */ 218 teq r3, #0x00000000 219 bne .Lswitch_search 220 221 /* if saving power, don't want to pageidlezero */ 222 mov r0, #0 223 adr lr, 3b 224 ldr pc, [r4, #(CF_SLEEP)] 225 /* loops back around */ 226 227 228/* 229 * Find a new lwp to run, save the current context and 230 * load the new context 231 * 232 * Arguments: 233 * r0 'struct lwp *' of the current LWP 234 */ 235 236ENTRY(cpu_switch) 237/* 238 * Local register usage. Some of these registers are out of date. 239 * r1 = oldlwp 240 * r2 = spl level 241 * r3 = whichqs 242 * r4 = queue 243 * r5 = &qs[queue] 244 * r6 = newlwp 245 * r7 = scratch 246 */ 247 stmfd sp!, {r4-r7, lr} 248 249 /* 250 * Indicate that there is no longer a valid process (curlwp = 0). 251 * Zero the current PCB pointer while we're at it. 252 */ 253 ldr r7, .Lcurlwp 254 ldr r6, .Lcurpcb 255 mov r2, #0x00000000 256 str r2, [r7] /* curproc = NULL */ 257 str r2, [r6] /* curpcb = NULL */ 258 259 /* stash the old proc while we call functions */ 260 mov r5, r0 261 262#if defined(MULTIPROCESSOR) || defined(LOCKDEBUG) 263 /* release the sched_lock before handling interrupts */ 264 bl _C_LABEL(sched_unlock_idle) 265#endif 266 267 /* Lower the spl level to spl0 and get the current spl level. */ 268#ifdef __NEWINTR 269 mov r0, #(IPL_NONE) 270 bl _C_LABEL(_spllower) 271#else /* ! __NEWINTR */ 272 mov r0, #(_SPL_0) 273 bl _C_LABEL(splx) 274#endif /* __NEWINTR */ 275 276 /* Push the old spl level onto the stack */ 277 str r0, [sp, #-0x0004]! 278 279 /* First phase : find a new lwp */ 280 281 ldr r7, .Lwhichqs 282 283 /* rem: r5 = old lwp */ 284 /* rem: r7 = &whichqs */ 285 286.Lswitch_search: 287 IRQdisable 288#if defined(MULTIPROCESSOR) || defined(LOCKDEBUG) 289 bl _C_LABEL(sched_lock_idle) 290#endif 291 292 /* Do we have any active queues */ 293 ldr r3, [r7] 294 295 /* If not we must idle until we do. */ 296 teq r3, #0x00000000 297 beq _ASM_LABEL(idle) 298 299 /* put old proc back in r1 */ 300 mov r1, r5 301 302 /* rem: r1 = old lwp */ 303 /* rem: r3 = whichqs */ 304 /* rem: interrupts are disabled */ 305 306 /* used further down, saves SA stall */ 307 ldr r6, .Lqs 308 309 /* 310 * We have found an active queue. Currently we do not know which queue 311 * is active just that one of them is. 312 */ 313 /* Non-Xscale version of the ffs algorithm devised by d.seal and 314 * posted to comp.sys.arm on 16 Feb 1994. 315 */ 316 rsb r5, r3, #0 317 ands r0, r3, r5 318 319#ifndef __XSCALE__ 320 adr r5, .Lcpu_switch_ffs_table 321 322 /* X = R0 */ 323 orr r4, r0, r0, lsl #4 /* r4 = X * 0x11 */ 324 orr r4, r4, r4, lsl #6 /* r4 = X * 0x451 */ 325 rsb r4, r4, r4, lsl #16 /* r4 = X * 0x0450fbaf */ 326 327 /* now lookup in table indexed on top 6 bits of a4 */ 328 ldrb r4, [ r5, r4, lsr #26 ] 329 330#else /* __XSCALE__ */ 331 clz r4, r0 332 rsb r4, r4, #31 333#endif /* __XSCALE__ */ 334 335 /* rem: r0 = bit mask of chosen queue (1 << r4) */ 336 /* rem: r1 = old lwp */ 337 /* rem: r3 = whichqs */ 338 /* rem: r4 = queue number */ 339 /* rem: interrupts are disabled */ 340 341 /* Get the address of the queue (&qs[queue]) */ 342 add r5, r6, r4, lsl #3 343 344 /* 345 * Get the lwp from the queue and place the next process in 346 * the queue at the head. This basically unlinks the lwp at 347 * the head of the queue. 348 */ 349 ldr r6, [r5, #(L_FORW)] 350 351 /* rem: r6 = new lwp */ 352 ldr r7, [r6, #(L_FORW)] 353 str r7, [r5, #(L_FORW)] 354 355 /* 356 * Test to see if the queue is now empty. If the head of the queue 357 * points to the queue itself then there are no more lwps in 358 * the queue. We can therefore clear the queue not empty flag held 359 * in r3. 360 */ 361 362 teq r5, r7 363 biceq r3, r3, r0 364 365 /* rem: r0 = bit mask of chosen queue (1 << r4) - NOT NEEDED AN MORE */ 366 367 /* Fix the back pointer for the lwp now at the head of the queue. */ 368 ldr r0, [r6, #(L_BACK)] 369 str r0, [r7, #(L_BACK)] 370 371 /* Update the RAM copy of the queue not empty flags word. */ 372 ldr r7, .Lwhichqs 373 str r3, [r7] 374 375 /* rem: r1 = old lwp */ 376 /* rem: r3 = whichqs - NOT NEEDED ANY MORE */ 377 /* rem: r4 = queue number - NOT NEEDED ANY MORE */ 378 /* rem: r6 = new lwp */ 379 /* rem: interrupts are disabled */ 380 381 /* Clear the want_resched flag */ 382 ldr r7, .Lwant_resched 383 mov r0, #0x00000000 384 str r0, [r7] 385 386 /* 387 * Clear the back pointer of the lwp we have removed from 388 * the head of the queue. The new lwp is isolated now. 389 */ 390 str r0, [r6, #(L_BACK)] 391 392#if defined(MULTIPROCESSOR) || defined(LOCKDEBUG) 393 /* 394 * unlock the sched_lock, but leave interrupts off, for now. 395 */ 396 mov r7, r1 397 bl _C_LABEL(sched_unlock_idle) 398 mov r1, r7 399#endif 400 401.Lswitch_resume: 402#ifdef MULTIPROCESSOR 403 /* XXX use curcpu() */ 404 ldr r0, .Lcpu_info_store 405 str r0, [r6, #(L_CPU)] 406#else 407 /* l->l_cpu initialized in fork1() for single-processor */ 408#endif 409 410 /* Process is now on a processor. */ 411 mov r0, #LSONPROC /* l->l_stat = LSONPROC */ 412 str r0, [r6, #(L_STAT)] 413 414 /* We have a new curlwp now so make a note it */ 415 ldr r7, .Lcurlwp 416 str r6, [r7] 417 418 /* Hook in a new pcb */ 419 ldr r7, .Lcurpcb 420 ldr r0, [r6, #(L_ADDR)] 421 str r0, [r7] 422 423 /* At this point we can allow IRQ's again. */ 424 IRQenable 425 426 /* rem: r1 = old lwp */ 427 /* rem: r4 = return value */ 428 /* rem: r6 = new process */ 429 /* rem: interrupts are enabled */ 430 431 /* 432 * If the new process is the same as the process that called 433 * cpu_switch() then we do not need to save and restore any 434 * contexts. This means we can make a quick exit. 435 * The test is simple if curlwp on entry (now in r1) is the 436 * same as the proc removed from the queue we can jump to the exit. 437 */ 438 teq r1, r6 439 moveq r4, #0x00000000 /* default to "didn't switch" */ 440 beq .Lswitch_return 441 442 /* 443 * At this point, we are guaranteed to be switching to 444 * a new lwp. 445 */ 446 mov r4, #0x00000001 447 448 /* Remember the old lwp in r0 */ 449 mov r0, r1 450 451 /* 452 * If the old lwp on entry to cpu_switch was zero then the 453 * process that called it was exiting. This means that we do 454 * not need to save the current context. Instead we can jump 455 * straight to restoring the context for the new process. 456 */ 457 teq r0, #0x00000000 458 beq .Lswitch_exited 459 460 /* rem: r0 = old lwp */ 461 /* rem: r4 = return value */ 462 /* rem: r6 = new process */ 463 /* rem: interrupts are enabled */ 464 465 /* Stage two : Save old context */ 466 467 /* Get the user structure for the old lwp. */ 468 ldr r1, [r0, #(L_ADDR)] 469 470 /* Save all the registers in the old lwp's pcb */ 471#ifndef __XSCALE__ 472 add r7, r1, #(PCB_R8) 473 stmia r7, {r8-r13} 474#else 475 strd r8, [r1, #(PCB_R8)] 476 strd r10, [r1, #(PCB_R10)] 477 strd r12, [r1, #(PCB_R12)] 478#endif 479 480 /* 481 * NOTE: We can now use r8-r13 until it is time to restore 482 * them for the new process. 483 */ 484 485 /* Remember the old PCB. */ 486 mov r8, r1 487 488 /* r1 now free! */ 489 490 /* Get the user structure for the new process in r9 */ 491 ldr r9, [r6, #(L_ADDR)] 492 493 /* 494 * This can be optimised... We know we want to go from SVC32 495 * mode to UND32 mode 496 */ 497 mrs r3, cpsr 498 bic r2, r3, #(PSR_MODE) 499 orr r2, r2, #(PSR_UND32_MODE | I32_bit) 500 msr cpsr_c, r2 501 502 str sp, [r8, #(PCB_UND_SP)] 503 504 msr cpsr_c, r3 /* Restore the old mode */ 505 506 /* rem: r0 = old lwp */ 507 /* rem: r4 = return value */ 508 /* rem: r6 = new process */ 509 /* rem: r8 = old PCB */ 510 /* rem: r9 = new PCB */ 511 /* rem: interrupts are enabled */ 512 513 /* What else needs to be saved Only FPA stuff when that is supported */ 514 515 /* Third phase : restore saved context */ 516 517 /* rem: r0 = old lwp */ 518 /* rem: r4 = return value */ 519 /* rem: r6 = new lwp */ 520 /* rem: r8 = old PCB */ 521 /* rem: r9 = new PCB */ 522 /* rem: interrupts are enabled */ 523 524 /* 525 * Get the new L1 table pointer into r11. If we're switching to 526 * an LWP with the same address space as the outgoing one, we can 527 * skip the cache purge and the TTB load. 528 * 529 * To avoid data dep stalls that would happen anyway, we try 530 * and get some useful work done in the mean time. 531 */ 532 ldr r10, [r8, #(PCB_PAGEDIR)] /* r10 = old L1 */ 533 ldr r11, [r9, #(PCB_PAGEDIR)] /* r11 = new L1 */ 534 535 ldr r0, [r8, #(PCB_DACR)] /* r0 = old DACR */ 536 ldr r1, [r9, #(PCB_DACR)] /* r1 = new DACR */ 537 ldr r8, [r9, #(PCB_CSTATE)] /* r8 = &new_pmap->pm_cstate */ 538 ldr r5, .Llast_cache_state_ptr /* Previous thread's cstate */ 539 540 teq r10, r11 /* Same L1? */ 541 ldr r5, [r5] 542 cmpeq r0, r1 /* Same DACR? */ 543 beq .Lcs_context_switched /* yes! */ 544 545 ldr r3, .Lblock_userspace_access 546 mov r12, #0 547 cmp r5, #0 /* No last vm? (switch_exit) */ 548 beq .Lcs_cache_purge_skipped /* No, we can skip cache flsh */ 549 550 mov r2, #DOMAIN_CLIENT 551 cmp r1, r2, lsl #(PMAP_DOMAIN_KERNEL * 2) /* Sw to kernel thread? */ 552 beq .Lcs_cache_purge_skipped /* Yup. Don't flush cache */ 553 554 cmp r5, r8 /* Same userland VM space? */ 555 ldrneb r12, [r5, #(CS_CACHE_ID)] /* Last VM space cache state */ 556 557 /* 558 * We're definately switching to a new userland VM space, 559 * and the previous userland VM space has yet to be flushed 560 * from the cache/tlb. 561 * 562 * r12 holds the previous VM space's cs_cache_id state 563 */ 564 tst r12, #0xff /* Test cs_cache_id */ 565 beq .Lcs_cache_purge_skipped /* VM space is not in cache */ 566 567 /* 568 * Definately need to flush the cache. 569 * Mark the old VM space as NOT being resident in the cache. 570 */ 571 mov r2, #0x00000000 572 strb r2, [r5, #(CS_CACHE_ID)] 573 strb r2, [r5, #(CS_CACHE_D)] 574 575 /* 576 * Don't allow user space access between the purge and the switch. 577 */ 578 mov r2, #0x00000001 579 str r2, [r3] 580 581 stmfd sp!, {r0-r3} 582 ldr r1, .Lcpufuncs 583 mov lr, pc 584 ldr pc, [r1, #CF_IDCACHE_WBINV_ALL] 585 ldmfd sp!, {r0-r3} 586 587.Lcs_cache_purge_skipped: 588 /* rem: r1 = new DACR */ 589 /* rem: r3 = &block_userspace_access */ 590 /* rem: r4 = return value */ 591 /* rem: r5 = &old_pmap->pm_cstate (or NULL) */ 592 /* rem: r6 = new lwp */ 593 /* rem: r8 = &new_pmap->pm_cstate */ 594 /* rem: r9 = new PCB */ 595 /* rem: r10 = old L1 */ 596 /* rem: r11 = new L1 */ 597 598 mov r2, #0x00000000 599 ldr r7, [r9, #(PCB_PL1VEC)] 600 601 /* 602 * At this point we need to kill IRQ's again. 603 * 604 * XXXSCW: Don't need to block FIQs if vectors have been relocated 605 */ 606 IRQdisableALL 607 608 /* 609 * Interrupts are disabled so we can allow user space accesses again 610 * as none will occur until interrupts are re-enabled after the 611 * switch. 612 */ 613 str r2, [r3] 614 615 /* 616 * Ensure the vector table is accessible by fixing up the L1 617 */ 618 cmp r7, #0 /* No need to fixup vector table? */ 619 ldrne r2, [r7] /* But if yes, fetch current value */ 620 ldrne r0, [r9, #(PCB_L1VEC)] /* Fetch new vector_page value */ 621 mcr p15, 0, r1, c3, c0, 0 /* Update DACR for new context */ 622 cmpne r2, r0 /* Stuffing the same value? */ 623#ifndef PMAP_INCLUDE_PTE_SYNC 624 strne r0, [r7] /* Nope, update it */ 625#else 626 beq .Lcs_same_vector 627 str r0, [r7] /* Otherwise, update it */ 628 629 /* 630 * Need to sync the cache to make sure that last store is 631 * visible to the MMU. 632 */ 633 ldr r2, .Lcpufuncs 634 mov r0, r7 635 mov r1, #4 636 mov lr, pc 637 ldr pc, [r2, #CF_DCACHE_WB_RANGE] 638 639.Lcs_same_vector: 640#endif /* PMAP_INCLUDE_PTE_SYNC */ 641 642 cmp r10, r11 /* Switching to the same L1? */ 643 ldr r10, .Lcpufuncs 644 beq .Lcs_same_l1 /* Yup. */ 645 646 /* 647 * Do a full context switch, including full TLB flush. 648 */ 649 mov r0, r11 650 mov lr, pc 651 ldr pc, [r10, #CF_CONTEXT_SWITCH] 652 653 /* 654 * Mark the old VM space as NOT being resident in the TLB 655 */ 656 mov r2, #0x00000000 657 cmp r5, #0 658 strneh r2, [r5, #(CS_TLB_ID)] 659 b .Lcs_context_switched 660 661 /* 662 * We're switching to a different process in the same L1. 663 * In this situation, we only need to flush the TLB for the 664 * vector_page mapping, and even then only if r7 is non-NULL. 665 */ 666.Lcs_same_l1: 667 cmp r7, #0 668 movne r0, #0 /* We *know* vector_page's VA is 0x0 */ 669 movne lr, pc 670 ldrne pc, [r10, #CF_TLB_FLUSHID_SE] 671 672.Lcs_context_switched: 673 /* rem: r8 = &new_pmap->pm_cstate */ 674 675 /* XXXSCW: Safe to re-enable FIQs here */ 676 677 /* 678 * The new VM space is live in the cache and TLB. 679 * Update its cache/tlb state, and if it's not the kernel 680 * pmap, update the 'last cache state' pointer. 681 */ 682 mov r2, #-1 683 ldr r5, .Lpmap_kernel_cstate 684 ldr r0, .Llast_cache_state_ptr 685 str r2, [r8, #(CS_ALL)] 686 cmp r5, r8 687 strne r8, [r0] 688 689 /* rem: r4 = return value */ 690 /* rem: r6 = new lwp */ 691 /* rem: r9 = new PCB */ 692 693 /* 694 * This can be optimised... We know we want to go from SVC32 695 * mode to UND32 mode 696 */ 697 mrs r3, cpsr 698 bic r2, r3, #(PSR_MODE) 699 orr r2, r2, #(PSR_UND32_MODE) 700 msr cpsr_c, r2 701 702 ldr sp, [r9, #(PCB_UND_SP)] 703 704 msr cpsr_c, r3 /* Restore the old mode */ 705 706 /* Restore all the save registers */ 707#ifndef __XSCALE__ 708 add r7, r9, #PCB_R8 709 ldmia r7, {r8-r13} 710 711 sub r7, r7, #PCB_R8 /* restore PCB pointer */ 712#else 713 mov r7, r9 714 ldr r8, [r7, #(PCB_R8)] 715 ldr r9, [r7, #(PCB_R9)] 716 ldr r10, [r7, #(PCB_R10)] 717 ldr r11, [r7, #(PCB_R11)] 718 ldr r12, [r7, #(PCB_R12)] 719 ldr r13, [r7, #(PCB_SP)] 720#endif 721 722 ldr r5, [r6, #(L_PROC)] /* fetch the proc for below */ 723 724 /* rem: r4 = return value */ 725 /* rem: r5 = new lwp's proc */ 726 /* rem: r6 = new lwp */ 727 /* rem: r7 = new pcb */ 728 729#ifdef ARMFPE 730 add r0, r7, #(USER_SIZE) & 0x00ff 731 add r0, r0, #(USER_SIZE) & 0xff00 732 bl _C_LABEL(arm_fpe_core_changecontext) 733#endif 734 735 /* We can enable interrupts again */ 736 IRQenableALL 737 738 /* rem: r4 = return value */ 739 /* rem: r5 = new lwp's proc */ 740 /* rem: r6 = new lwp */ 741 /* rem: r7 = new PCB */ 742 743 /* 744 * Check for restartable atomic sequences (RAS). 745 */ 746 747 ldr r2, [r5, #(P_NRAS)] 748 ldr r4, [r7, #(PCB_TF)] /* r4 = trapframe (used below) */ 749 teq r2, #0 /* p->p_nras == 0? */ 750 bne .Lswitch_do_ras /* no, check for one */ 751 752.Lswitch_return: 753 754 /* Get the spl level from the stack and update the current spl level */ 755 ldr r0, [sp], #0x0004 756 bl _C_LABEL(splx) 757 758 /* cpu_switch returns 1 == switched, 0 == didn't switch */ 759 mov r0, r4 760 761 /* 762 * Pull the registers that got pushed when either savectx() or 763 * cpu_switch() was called and return. 764 */ 765 ldmfd sp!, {r4-r7, pc} 766 767.Lswitch_do_ras: 768 ldr r1, [r4, #(TF_PC)] /* second ras_lookup() arg */ 769 mov r0, r5 /* first ras_lookup() arg */ 770 bl _C_LABEL(ras_lookup) 771 cmn r0, #1 /* -1 means "not in a RAS" */ 772 strne r0, [r4, #(TF_PC)] 773 b .Lswitch_return 774 775.Lswitch_exited: 776 /* 777 * We skip the cache purge because switch_exit() already did it. 778 * Load up registers the way .Lcs_cache_purge_skipped expects. 779 * Userpsace access already blocked by switch_exit(). 780 */ 781 ldr r9, [r6, #(L_ADDR)] /* r9 = new PCB */ 782 ldr r3, .Lblock_userspace_access 783 mrc p15, 0, r10, c2, c0, 0 /* r10 = old L1 */ 784 mov r5, #0 /* No previous cache state */ 785 ldr r1, [r9, #(PCB_DACR)] /* r1 = new DACR */ 786 ldr r8, [r9, #(PCB_CSTATE)] /* r8 = new cache state */ 787 ldr r11, [r9, #(PCB_PAGEDIR)] /* r11 = new L1 */ 788 b .Lcs_cache_purge_skipped 789 790/* 791 * cpu_switchto(struct lwp *current, struct lwp *next) 792 * Switch to the specified next LWP 793 * Arguments: 794 * 795 * r0 'struct lwp *' of the current LWP 796 * r1 'struct lwp *' of the LWP to switch to 797 */ 798ENTRY(cpu_switchto) 799 stmfd sp!, {r4-r7, lr} 800 801 /* Lower the spl level to spl0 and get the current spl level. */ 802 mov r6, r0 /* save old lwp */ 803 mov r5, r1 /* save new lwp */ 804 805#if defined(LOCKDEBUG) 806 /* release the sched_lock before handling interrupts */ 807 bl _C_LABEL(sched_unlock_idle) 808#endif 809 810#ifdef __NEWINTR 811 mov r0, #(IPL_NONE) 812 bl _C_LABEL(_spllower) 813#else /* ! __NEWINTR */ 814#ifdef spl0 815 mov r0, #(_SPL_0) 816 bl _C_LABEL(splx) 817#else 818 bl _C_LABEL(spl0) 819#endif /* spl0 */ 820#endif /* __NEWINTR */ 821 822 /* Push the old spl level onto the stack */ 823 str r0, [sp, #-0x0004]! 824 825 IRQdisable 826#if defined(LOCKDEBUG) 827 bl _C_LABEL(sched_lock_idle) 828#endif 829 830 mov r0, r6 /* restore old lwp */ 831 mov r1, r5 /* restore new lwp */ 832 833 /* rem: r0 = old lwp */ 834 /* rem: r1 = new lwp */ 835 /* rem: interrupts are disabled */ 836 837 /* 838 * Okay, set up registers the way cpu_switch() wants them, 839 * and jump into the middle of it (where we bring up the 840 * new process). 841 */ 842 mov r6, r1 /* r6 = new lwp */ 843#if defined(LOCKDEBUG) 844 mov r5, r0 /* preserve old lwp */ 845 bl _C_LABEL(sched_unlock_idle) 846 mov r1, r5 /* r1 = old lwp */ 847#else 848 mov r1, r0 /* r1 = old lwp */ 849#endif 850 b .Lswitch_resume 851 852/* 853 * void switch_exit(struct lwp *l, struct lwp *l0, void (*exit)(struct lwp *)); 854 * Switch to lwp0's saved context and deallocate the address space and kernel 855 * stack for l. Then jump into cpu_switch(), as if we were in lwp0 all along. 856 */ 857 858/* LINTSTUB: Func: void switch_exit(struct lwp *l, struct lwp *l0, void (*func)(struct lwp *)) */ 859ENTRY(switch_exit) 860 /* 861 * The process is going away, so we can use callee-saved 862 * registers here without having to save them. 863 */ 864 865 mov r4, r0 866 ldr r0, .Lcurlwp 867 868 mov r5, r1 869 ldr r1, .Lblock_userspace_access 870 871 mov r6, r2 872 873 /* 874 * r4 = lwp 875 * r5 = lwp0 876 * r6 = exit func 877 */ 878 879 mov r2, #0x00000000 /* curlwp = NULL */ 880 str r2, [r0] 881 882 /* 883 * We're about to clear both the cache and the TLB. 884 * Make sure to zap the 'last cache state' pointer since the 885 * pmap might be about to go away. Also ensure the outgoing 886 * VM space's cache state is marked as NOT resident in the 887 * cache, and that lwp0's cache state IS resident. 888 */ 889 ldr r7, [r4, #(L_ADDR)] /* r7 = old lwp's PCB */ 890 ldr r0, .Llast_cache_state_ptr /* Last userland cache state */ 891 ldr r9, [r7, #(PCB_CSTATE)] /* Fetch cache state pointer */ 892 ldr r3, [r5, #(L_ADDR)] /* r3 = lwp0's PCB */ 893 str r2, [r0] /* No previous cache state */ 894 str r2, [r9, #(CS_ALL)] /* Zap old lwp's cache state */ 895 ldr r3, [r3, #(PCB_CSTATE)] /* lwp0's cache state */ 896 mov r2, #-1 897 str r2, [r3, #(CS_ALL)] /* lwp0 is in da cache! */ 898 899 /* 900 * Don't allow user space access between the purge and the switch. 901 */ 902 mov r2, #0x00000001 903 str r2, [r1] 904 905 /* Switch to lwp0 context */ 906 907 ldr r9, .Lcpufuncs 908 mov lr, pc 909 ldr pc, [r9, #CF_IDCACHE_WBINV_ALL] 910 911 ldr r0, [r7, #(PCB_PL1VEC)] 912 ldr r1, [r7, #(PCB_DACR)] 913 914 /* 915 * r0 = Pointer to L1 slot for vector_page (or NULL) 916 * r1 = lwp0's DACR 917 * r4 = lwp we're switching from 918 * r5 = lwp0 919 * r6 = exit func 920 * r7 = lwp0's PCB 921 * r9 = cpufuncs 922 */ 923 924 IRQdisableALL 925 926 /* 927 * Ensure the vector table is accessible by fixing up lwp0's L1 928 */ 929 cmp r0, #0 /* No need to fixup vector table? */ 930 ldrne r3, [r0] /* But if yes, fetch current value */ 931 ldrne r2, [r7, #(PCB_L1VEC)] /* Fetch new vector_page value */ 932 mcr p15, 0, r1, c3, c0, 0 /* Update DACR for lwp0's context */ 933 cmpne r3, r2 /* Stuffing the same value? */ 934 strne r2, [r0] /* Store if not. */ 935 936#ifdef PMAP_INCLUDE_PTE_SYNC 937 /* 938 * Need to sync the cache to make sure that last store is 939 * visible to the MMU. 940 */ 941 movne r1, #4 942 movne lr, pc 943 ldrne pc, [r9, #CF_DCACHE_WB_RANGE] 944#endif /* PMAP_INCLUDE_PTE_SYNC */ 945 946 /* 947 * Note: We don't do the same optimisation as cpu_switch() with 948 * respect to avoiding flushing the TLB if we're switching to 949 * the same L1 since this process' VM space may be about to go 950 * away, so we don't want *any* turds left in the TLB. 951 */ 952 953 /* Switch the memory to the new process */ 954 ldr r0, [r7, #(PCB_PAGEDIR)] 955 mov lr, pc 956 ldr pc, [r9, #CF_CONTEXT_SWITCH] 957 958 ldr r0, .Lcurpcb 959 960 /* Restore all the save registers */ 961#ifndef __XSCALE__ 962 add r1, r7, #PCB_R8 963 ldmia r1, {r8-r13} 964#else 965 ldr r8, [r7, #(PCB_R8)] 966 ldr r9, [r7, #(PCB_R9)] 967 ldr r10, [r7, #(PCB_R10)] 968 ldr r11, [r7, #(PCB_R11)] 969 ldr r12, [r7, #(PCB_R12)] 970 ldr r13, [r7, #(PCB_SP)] 971#endif 972 str r7, [r0] /* curpcb = lwp0's PCB */ 973 974 IRQenableALL 975 976 /* 977 * Schedule the vmspace and stack to be freed. 978 */ 979 mov r0, r4 /* {lwp_}exit2(l) */ 980 mov lr, pc 981 mov pc, r6 982 983 ldr r7, .Lwhichqs /* r7 = &whichqs */ 984 mov r5, #0x00000000 /* r5 = old lwp = NULL */ 985 b .Lswitch_search 986 987/* LINTSTUB: Func: void savectx(struct pcb *pcb) */ 988ENTRY(savectx) 989 /* 990 * r0 = pcb 991 */ 992 993 /* Push registers.*/ 994 stmfd sp!, {r4-r7, lr} 995 996 /* Store all the registers in the process's pcb */ 997#ifndef __XSCALE__ 998 add r2, r0, #(PCB_R8) 999 stmia r2, {r8-r13} 1000#else 1001 strd r8, [r0, #(PCB_R8)] 1002 strd r10, [r0, #(PCB_R10)] 1003 strd r12, [r0, #(PCB_R12)] 1004#endif 1005 1006 /* Pull the regs of the stack */ 1007 ldmfd sp!, {r4-r7, pc} 1008 1009ENTRY(proc_trampoline) 1010#ifdef MULTIPROCESSOR 1011 bl _C_LABEL(proc_trampoline_mp) 1012#endif 1013 mov r0, r5 1014 mov r1, sp 1015 mov lr, pc 1016 mov pc, r4 1017 1018 /* Kill irq's */ 1019 mrs r0, cpsr 1020 orr r0, r0, #(I32_bit) 1021 msr cpsr_c, r0 1022 1023 PULLFRAME 1024 1025 movs pc, lr /* Exit */ 1026 1027#ifndef __XSCALE__ 1028 .type .Lcpu_switch_ffs_table, _ASM_TYPE_OBJECT; 1029.Lcpu_switch_ffs_table: 1030/* same as ffs table but all nums are -1 from that */ 1031/* 0 1 2 3 4 5 6 7 */ 1032 .byte 0, 0, 1, 12, 2, 6, 0, 13 /* 0- 7 */ 1033 .byte 3, 0, 7, 0, 0, 0, 0, 14 /* 8-15 */ 1034 .byte 10, 4, 0, 0, 8, 0, 0, 25 /* 16-23 */ 1035 .byte 0, 0, 0, 0, 0, 21, 27, 15 /* 24-31 */ 1036 .byte 31, 11, 5, 0, 0, 0, 0, 0 /* 32-39 */ 1037 .byte 9, 0, 0, 24, 0, 0, 20, 26 /* 40-47 */ 1038 .byte 30, 0, 0, 0, 0, 23, 0, 19 /* 48-55 */ 1039 .byte 29, 0, 22, 18, 28, 17, 16, 0 /* 56-63 */ 1040#endif /* !__XSCALE_ */ 1041