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