cpuswitch.S revision 1.24 1 1.24 bjh21 /* $NetBSD: cpuswitch.S,v 1.24 2002/10/14 22:32:50 bjh21 Exp $ */
2 1.1 chris
3 1.1 chris /*
4 1.1 chris * Copyright (c) 1994-1998 Mark Brinicombe.
5 1.1 chris * Copyright (c) 1994 Brini.
6 1.1 chris * All rights reserved.
7 1.1 chris *
8 1.1 chris * This code is derived from software written for Brini by Mark Brinicombe
9 1.1 chris *
10 1.1 chris * Redistribution and use in source and binary forms, with or without
11 1.1 chris * modification, are permitted provided that the following conditions
12 1.1 chris * are met:
13 1.1 chris * 1. Redistributions of source code must retain the above copyright
14 1.1 chris * notice, this list of conditions and the following disclaimer.
15 1.1 chris * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 chris * notice, this list of conditions and the following disclaimer in the
17 1.1 chris * documentation and/or other materials provided with the distribution.
18 1.1 chris * 3. All advertising materials mentioning features or use of this software
19 1.1 chris * must display the following acknowledgement:
20 1.1 chris * This product includes software developed by Brini.
21 1.1 chris * 4. The name of the company nor the name of the author may be used to
22 1.1 chris * endorse or promote products derived from this software without specific
23 1.1 chris * prior written permission.
24 1.1 chris *
25 1.1 chris * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
26 1.1 chris * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
27 1.1 chris * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28 1.1 chris * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 1.1 chris * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 1.1 chris * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 1.1 chris * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 1.1 chris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 1.1 chris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 1.1 chris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 1.1 chris * SUCH DAMAGE.
36 1.1 chris *
37 1.1 chris * RiscBSD kernel project
38 1.1 chris *
39 1.1 chris * cpuswitch.S
40 1.1 chris *
41 1.1 chris * cpu switching functions
42 1.1 chris *
43 1.1 chris * Created : 15/10/94
44 1.1 chris */
45 1.1 chris
46 1.1 chris #include "opt_armfpe.h"
47 1.19 bjh21 #include "opt_multiprocessor.h"
48 1.1 chris
49 1.1 chris #include "assym.h"
50 1.1 chris #include <machine/param.h>
51 1.1 chris #include <machine/cpu.h>
52 1.1 chris #include <machine/frame.h>
53 1.1 chris #include <machine/asm.h>
54 1.1 chris
55 1.1 chris #undef IRQdisable
56 1.1 chris #undef IRQenable
57 1.1 chris
58 1.1 chris /*
59 1.1 chris * New experimental definitions of IRQdisable and IRQenable
60 1.1 chris * These keep FIQ's enabled since FIQ's are special.
61 1.1 chris */
62 1.1 chris
63 1.1 chris #define IRQdisable \
64 1.13 thorpej mrs r14, cpsr ; \
65 1.1 chris orr r14, r14, #(I32_bit) ; \
66 1.13 thorpej msr cpsr_c, r14 ; \
67 1.1 chris
68 1.1 chris #define IRQenable \
69 1.13 thorpej mrs r14, cpsr ; \
70 1.1 chris bic r14, r14, #(I32_bit) ; \
71 1.13 thorpej msr cpsr_c, r14 ; \
72 1.1 chris
73 1.1 chris /*
74 1.1 chris * setrunqueue() and remrunqueue()
75 1.1 chris *
76 1.1 chris * Functions to add and remove a process for the run queue.
77 1.1 chris */
78 1.1 chris
79 1.1 chris .text
80 1.1 chris
81 1.17 thorpej .Lwhichqs:
82 1.1 chris .word _C_LABEL(sched_whichqs)
83 1.1 chris
84 1.17 thorpej .Lqs:
85 1.1 chris .word _C_LABEL(sched_qs)
86 1.1 chris
87 1.1 chris /*
88 1.1 chris * On entry
89 1.1 chris * r0 = process
90 1.1 chris */
91 1.1 chris
92 1.1 chris ENTRY(setrunqueue)
93 1.1 chris /*
94 1.1 chris * Local register usage
95 1.1 chris * r0 = process
96 1.1 chris * r1 = queue
97 1.1 chris * r2 = &qs[queue] and temp
98 1.1 chris * r3 = temp
99 1.1 chris * r12 = whichqs
100 1.1 chris */
101 1.1 chris #ifdef DIAGNOSTIC
102 1.1 chris ldr r1, [r0, #(P_BACK)]
103 1.1 chris teq r1, #0x00000000
104 1.14 briggs bne .Lsetrunqueue_erg
105 1.1 chris
106 1.1 chris ldr r1, [r0, #(P_WCHAN)]
107 1.1 chris teq r1, #0x00000000
108 1.14 briggs bne .Lsetrunqueue_erg
109 1.1 chris #endif
110 1.1 chris
111 1.1 chris /* Get the priority of the queue */
112 1.1 chris ldrb r1, [r0, #(P_PRIORITY)]
113 1.1 chris
114 1.1 chris /* Indicate that there is a process on this queue */
115 1.17 thorpej ldr r12, .Lwhichqs
116 1.12 chris mov r1, r1, lsr #2
117 1.1 chris ldr r2, [r12]
118 1.1 chris mov r3, #0x00000001
119 1.1 chris mov r3, r3, lsl r1
120 1.1 chris orr r2, r2, r3
121 1.1 chris str r2, [r12]
122 1.1 chris
123 1.1 chris /* Get the address of the queue */
124 1.17 thorpej ldr r2, .Lqs
125 1.1 chris add r1, r2, r1, lsl # 3
126 1.1 chris
127 1.1 chris /* Hook the process in */
128 1.1 chris str r1, [r0, #(P_FORW)]
129 1.1 chris ldr r2, [r1, #(P_BACK)]
130 1.1 chris
131 1.1 chris str r0, [r1, #(P_BACK)]
132 1.1 chris #ifdef DIAGNOSTIC
133 1.1 chris teq r2, #0x00000000
134 1.14 briggs beq .Lsetrunqueue_erg
135 1.1 chris #endif
136 1.1 chris str r0, [r2, #(P_FORW)]
137 1.1 chris str r2, [r0, #(P_BACK)]
138 1.1 chris
139 1.1 chris mov pc, lr
140 1.1 chris
141 1.1 chris #ifdef DIAGNOSTIC
142 1.14 briggs .Lsetrunqueue_erg:
143 1.1 chris mov r2, r1
144 1.1 chris mov r1, r0
145 1.23 bjh21 adr r0, .Ltext1
146 1.1 chris bl _C_LABEL(printf)
147 1.1 chris
148 1.17 thorpej ldr r2, .Lqs
149 1.1 chris ldr r1, [r2]
150 1.23 bjh21 adr r0, .Ltext2
151 1.1 chris b _C_LABEL(panic)
152 1.1 chris
153 1.17 thorpej .Ltext1:
154 1.1 chris .asciz "setrunqueue : %08x %08x\n"
155 1.17 thorpej .Ltext2:
156 1.1 chris .asciz "setrunqueue : [qs]=%08x qs=%08x\n"
157 1.1 chris .align 0
158 1.1 chris #endif
159 1.1 chris
160 1.1 chris /*
161 1.1 chris * On entry
162 1.1 chris * r0 = process
163 1.1 chris */
164 1.1 chris
165 1.1 chris ENTRY(remrunqueue)
166 1.1 chris /*
167 1.1 chris * Local register usage
168 1.1 chris * r0 = oldproc
169 1.1 chris * r1 = queue
170 1.1 chris * r2 = &qs[queue] and scratch
171 1.1 chris * r3 = scratch
172 1.1 chris * r12 = whichqs
173 1.1 chris */
174 1.1 chris
175 1.1 chris /* Get the priority of the queue */
176 1.1 chris ldrb r1, [r0, #(P_PRIORITY)]
177 1.1 chris mov r1, r1, lsr #2
178 1.1 chris
179 1.1 chris /* Unhook the process */
180 1.1 chris ldr r2, [r0, #(P_FORW)]
181 1.1 chris ldr r3, [r0, #(P_BACK)]
182 1.1 chris
183 1.1 chris str r3, [r2, #(P_BACK)]
184 1.1 chris str r2, [r3, #(P_FORW)]
185 1.1 chris
186 1.1 chris /* If the queue is now empty clear the queue not empty flag */
187 1.1 chris teq r2, r3
188 1.1 chris
189 1.1 chris /* This could be reworked to avoid the use of r4 */
190 1.17 thorpej ldreq r12, .Lwhichqs
191 1.12 chris moveq r3, #0x00000001
192 1.1 chris ldreq r2, [r12]
193 1.1 chris moveq r3, r3, lsl r1
194 1.1 chris biceq r2, r2, r3
195 1.1 chris streq r2, [r12]
196 1.1 chris
197 1.1 chris /* Remove the back pointer for the process */
198 1.1 chris mov r1, #0x00000000
199 1.1 chris str r1, [r0, #(P_BACK)]
200 1.1 chris
201 1.1 chris mov pc, lr
202 1.1 chris
203 1.1 chris
204 1.1 chris /*
205 1.1 chris * cpuswitch()
206 1.1 chris *
207 1.1 chris * preforms a process context switch.
208 1.1 chris * This function has several entry points
209 1.1 chris */
210 1.1 chris
211 1.19 bjh21 #ifdef MULTIPROCESSOR
212 1.19 bjh21 .Lcpu_info_store:
213 1.19 bjh21 .word _C_LABEL(cpu_info_store)
214 1.19 bjh21 .Lcurproc:
215 1.19 bjh21 /* FIXME: This is bogus in the general case. */
216 1.19 bjh21 .word _C_LABEL(cpu_info_store) + CI_CURPROC
217 1.22 bjh21
218 1.22 bjh21 .Lcurpcb:
219 1.22 bjh21 .word _C_LABEL(cpu_info_store) + CI_CURPCB
220 1.19 bjh21 #else
221 1.17 thorpej .Lcurproc:
222 1.1 chris .word _C_LABEL(curproc)
223 1.1 chris
224 1.17 thorpej .Lcurpcb:
225 1.1 chris .word _C_LABEL(curpcb)
226 1.22 bjh21 #endif
227 1.1 chris
228 1.17 thorpej .Lwant_resched:
229 1.1 chris .word _C_LABEL(want_resched)
230 1.1 chris
231 1.17 thorpej .Lcpufuncs:
232 1.1 chris .word _C_LABEL(cpufuncs)
233 1.1 chris
234 1.22 bjh21 #ifndef MULTIPROCESSOR
235 1.1 chris .data
236 1.1 chris .global _C_LABEL(curpcb)
237 1.1 chris _C_LABEL(curpcb):
238 1.1 chris .word 0x00000000
239 1.1 chris .text
240 1.22 bjh21 #endif
241 1.1 chris
242 1.17 thorpej .Lblock_userspace_access:
243 1.1 chris .word _C_LABEL(block_userspace_access)
244 1.1 chris
245 1.15 thorpej .Lcpu_do_powersave:
246 1.15 thorpej .word _C_LABEL(cpu_do_powersave)
247 1.15 thorpej
248 1.1 chris /*
249 1.1 chris * Idle loop, exercised while waiting for a process to wake up.
250 1.16 thorpej *
251 1.16 thorpej * NOTE: When we jump back to .Lswitch_search, we must have a
252 1.16 thorpej * pointer to whichqs in r7, which is what it is when we arrive
253 1.16 thorpej * here.
254 1.1 chris */
255 1.7 chris /* LINTSTUB: Ignore */
256 1.4 chris ASENTRY_NP(idle)
257 1.19 bjh21 #if defined(MULTIPROCESSOR) || defined(LOCKDEBUG)
258 1.7 chris bl _C_LABEL(sched_unlock_idle)
259 1.7 chris #endif
260 1.16 thorpej ldr r3, .Lcpu_do_powersave
261 1.16 thorpej
262 1.1 chris /* Enable interrupts */
263 1.1 chris IRQenable
264 1.1 chris
265 1.15 thorpej /* If we don't want to sleep, use a simpler loop. */
266 1.16 thorpej ldr r3, [r3] /* r3 = cpu_do_powersave */
267 1.15 thorpej teq r3, #0
268 1.16 thorpej bne 2f
269 1.16 thorpej
270 1.16 thorpej /* Non-powersave idle. */
271 1.16 thorpej 1: /* should maybe do uvm pageidlezero stuff here */
272 1.16 thorpej ldr r3, [r7] /* r3 = whichqs */
273 1.16 thorpej teq r3, #0x00000000
274 1.16 thorpej bne .Lswitch_search
275 1.16 thorpej b 1b
276 1.15 thorpej
277 1.16 thorpej 2: /* Powersave idle. */
278 1.17 thorpej ldr r4, .Lcpufuncs
279 1.16 thorpej 3: ldr r3, [r7] /* r3 = whichqs */
280 1.15 thorpej teq r3, #0x00000000
281 1.15 thorpej bne .Lswitch_search
282 1.15 thorpej
283 1.15 thorpej /* if saving power, don't want to pageidlezero */
284 1.1 chris mov r0, #0
285 1.21 bjh21 adr lr, 3b
286 1.15 thorpej ldr pc, [r4, #(CF_SLEEP)]
287 1.15 thorpej /* loops back around */
288 1.15 thorpej
289 1.1 chris
290 1.1 chris /*
291 1.1 chris * Find a new process to run, save the current context and
292 1.1 chris * load the new context
293 1.1 chris */
294 1.1 chris
295 1.1 chris ENTRY(cpu_switch)
296 1.1 chris /*
297 1.1 chris * Local register usage. Some of these registers are out of date.
298 1.1 chris * r1 = oldproc
299 1.1 chris * r3 = whichqs
300 1.1 chris * r4 = queue
301 1.1 chris * r5 = &qs[queue]
302 1.1 chris * r6 = newproc
303 1.1 chris * r7 = scratch
304 1.1 chris */
305 1.1 chris stmfd sp!, {r4-r7, lr}
306 1.1 chris
307 1.1 chris /*
308 1.1 chris * Get the current process and indicate that there is no longer
309 1.11 thorpej * a valid process (curproc = 0). Zero the current PCB pointer
310 1.11 thorpej * while we're at it.
311 1.1 chris */
312 1.17 thorpej ldr r7, .Lcurproc
313 1.17 thorpej ldr r6, .Lcurpcb
314 1.1 chris mov r0, #0x00000000
315 1.11 thorpej ldr r1, [r7] /* r1 = curproc */
316 1.11 thorpej str r0, [r7] /* curproc = NULL */
317 1.11 thorpej str r0, [r6] /* curpcb = NULL */
318 1.1 chris
319 1.10 thorpej /* stash the old proc while we call functions */
320 1.10 thorpej mov r5, r1
321 1.1 chris
322 1.19 bjh21 #if defined(MULTIPROCESSOR) || defined(LOCKDEBUG)
323 1.7 chris /* release the sched_lock before handling interrupts */
324 1.7 chris bl _C_LABEL(sched_unlock_idle)
325 1.7 chris #endif
326 1.7 chris
327 1.7 chris /* Lower the spl level to spl0 and get the current spl level. */
328 1.5 thorpej #ifdef __NEWINTR
329 1.5 thorpej mov r0, #(IPL_NONE)
330 1.5 thorpej bl _C_LABEL(_spllower)
331 1.5 thorpej #else /* ! __NEWINTR */
332 1.1 chris #ifdef spl0
333 1.1 chris mov r0, #(_SPL_0)
334 1.1 chris bl _C_LABEL(splx)
335 1.1 chris #else
336 1.1 chris bl _C_LABEL(spl0)
337 1.5 thorpej #endif /* spl0 */
338 1.5 thorpej #endif /* __NEWINTR */
339 1.1 chris
340 1.1 chris /* Push the old spl level onto the stack */
341 1.1 chris str r0, [sp, #-0x0004]!
342 1.1 chris
343 1.1 chris /* First phase : find a new process */
344 1.1 chris
345 1.17 thorpej ldr r7, .Lwhichqs
346 1.16 thorpej
347 1.7 chris /* rem: r5 = old proc */
348 1.16 thorpej /* rem: r7 = &whichqs */
349 1.7 chris
350 1.14 briggs .Lswitch_search:
351 1.1 chris IRQdisable
352 1.19 bjh21 #if defined(MULTIPROCESSOR) || defined(LOCKDEBUG)
353 1.7 chris bl _C_LABEL(sched_lock_idle)
354 1.7 chris #endif
355 1.7 chris
356 1.1 chris /* Do we have any active queues */
357 1.1 chris ldr r3, [r7]
358 1.1 chris
359 1.1 chris /* If not we must idle until we do. */
360 1.1 chris teq r3, #0x00000000
361 1.4 chris beq _ASM_LABEL(idle)
362 1.7 chris
363 1.10 thorpej /* put old proc back in r1 */
364 1.7 chris mov r1, r5
365 1.1 chris
366 1.1 chris /* rem: r1 = old proc */
367 1.1 chris /* rem: r3 = whichqs */
368 1.1 chris /* rem: interrupts are disabled */
369 1.1 chris
370 1.1 chris /*
371 1.1 chris * We have found an active queue. Currently we do not know which queue
372 1.1 chris * is active just that one of them is.
373 1.1 chris */
374 1.1 chris /* this is the ffs algorithm devised by d.seal and posted to
375 1.1 chris * comp.sys.arm on 16 Feb 1994.
376 1.1 chris */
377 1.1 chris rsb r5, r3, #0
378 1.1 chris ands r0, r3, r5
379 1.1 chris
380 1.17 thorpej adr r5, .Lcpu_switch_ffs_table
381 1.1 chris
382 1.3 chris /* X = R0 */
383 1.3 chris orr r4, r0, r0, lsl #4 /* r4 = X * 0x11 */
384 1.3 chris orr r4, r4, r4, lsl #6 /* r4 = X * 0x451 */
385 1.3 chris rsb r4, r4, r4, lsl #16 /* r4 = X * 0x0450fbaf */
386 1.1 chris
387 1.1 chris /* used further down, saves SA stall */
388 1.17 thorpej ldr r6, .Lqs
389 1.1 chris
390 1.3 chris /* now lookup in table indexed on top 6 bits of a4 */
391 1.1 chris ldrb r4, [ r5, r4, lsr #26 ]
392 1.1 chris
393 1.1 chris /* rem: r0 = bit mask of chosen queue (1 << r4) */
394 1.1 chris /* rem: r1 = old proc */
395 1.1 chris /* rem: r3 = whichqs */
396 1.1 chris /* rem: r4 = queue number */
397 1.1 chris /* rem: interrupts are disabled */
398 1.1 chris
399 1.1 chris /* Get the address of the queue (&qs[queue]) */
400 1.1 chris add r5, r6, r4, lsl #3
401 1.1 chris
402 1.1 chris /*
403 1.1 chris * Get the process from the queue and place the next process in
404 1.1 chris * the queue at the head. This basically unlinks the process at
405 1.1 chris * the head of the queue.
406 1.1 chris */
407 1.1 chris ldr r6, [r5, #(P_FORW)]
408 1.1 chris
409 1.1 chris /* rem: r6 = new process */
410 1.1 chris ldr r7, [r6, #(P_FORW)]
411 1.1 chris str r7, [r5, #(P_FORW)]
412 1.1 chris
413 1.1 chris /*
414 1.1 chris * Test to see if the queue is now empty. If the head of the queue
415 1.1 chris * points to the queue itself then there are no more processes in
416 1.1 chris * the queue. We can therefore clear the queue not empty flag held
417 1.1 chris * in r3.
418 1.1 chris */
419 1.1 chris
420 1.1 chris teq r5, r7
421 1.1 chris biceq r3, r3, r0
422 1.1 chris
423 1.1 chris /* rem: r0 = bit mask of chosen queue (1 << r4) - NOT NEEDED AN MORE */
424 1.1 chris
425 1.1 chris /* Fix the back pointer for the process now at the head of the queue. */
426 1.1 chris ldr r0, [r6, #(P_BACK)]
427 1.1 chris str r0, [r7, #(P_BACK)]
428 1.1 chris
429 1.1 chris /* Update the RAM copy of the queue not empty flags word. */
430 1.17 thorpej ldr r7, .Lwhichqs
431 1.1 chris str r3, [r7]
432 1.1 chris
433 1.1 chris /* rem: r1 = old proc */
434 1.1 chris /* rem: r3 = whichqs - NOT NEEDED ANY MORE */
435 1.1 chris /* rem: r4 = queue number - NOT NEEDED ANY MORE */
436 1.1 chris /* rem: r6 = new process */
437 1.1 chris /* rem: interrupts are disabled */
438 1.1 chris
439 1.1 chris /* Clear the want_resched flag */
440 1.17 thorpej ldr r7, .Lwant_resched
441 1.1 chris mov r0, #0x00000000
442 1.1 chris str r0, [r7]
443 1.1 chris
444 1.1 chris /*
445 1.1 chris * Clear the back pointer of the process we have removed from
446 1.1 chris * the head of the queue. The new process is isolated now.
447 1.1 chris */
448 1.1 chris str r0, [r6, #(P_BACK)]
449 1.1 chris
450 1.19 bjh21 #if defined(MULTIPROCESSOR) || defined(LOCKDEBUG)
451 1.7 chris /*
452 1.7 chris * unlock the sched_lock, but leave interrupts off, for now.
453 1.7 chris */
454 1.7 chris mov r7, r1
455 1.7 chris bl _C_LABEL(sched_unlock_idle)
456 1.7 chris mov r1, r7
457 1.7 chris #endif
458 1.7 chris
459 1.19 bjh21 #ifdef MULTIPROCESSOR
460 1.19 bjh21 /* XXX use curcpu() */
461 1.19 bjh21 ldr r0, .Lcpu_info_store
462 1.19 bjh21 str r0, [r6, #(P_CPU)]
463 1.19 bjh21 #else
464 1.1 chris /* p->p_cpu initialized in fork1() for single-processor */
465 1.19 bjh21 #endif
466 1.1 chris
467 1.1 chris /* Process is now on a processor. */
468 1.1 chris mov r0, #SONPROC /* p->p_stat = SONPROC */
469 1.1 chris strb r0, [r6, #(P_STAT)]
470 1.1 chris
471 1.1 chris /* We have a new curproc now so make a note it */
472 1.17 thorpej ldr r7, .Lcurproc
473 1.1 chris str r6, [r7]
474 1.1 chris
475 1.1 chris /* Hook in a new pcb */
476 1.17 thorpej ldr r7, .Lcurpcb
477 1.1 chris ldr r0, [r6, #(P_ADDR)]
478 1.1 chris str r0, [r7]
479 1.1 chris
480 1.1 chris /* At this point we can allow IRQ's again. */
481 1.1 chris IRQenable
482 1.1 chris
483 1.1 chris /* rem: r1 = old proc */
484 1.1 chris /* rem: r6 = new process */
485 1.4 chris /* rem: interrupts are enabled */
486 1.1 chris
487 1.1 chris /*
488 1.1 chris * If the new process is the same as the process that called
489 1.1 chris * cpu_switch() then we do not need to save and restore any
490 1.1 chris * contexts. This means we can make a quick exit.
491 1.1 chris * The test is simple if curproc on entry (now in r1) is the
492 1.1 chris * same as the proc removed from the queue we can jump to the exit.
493 1.1 chris */
494 1.1 chris teq r1, r6
495 1.14 briggs beq .Lswitch_return
496 1.1 chris
497 1.9 thorpej /* Remember the old process in r0 */
498 1.9 thorpej mov r0, r1
499 1.9 thorpej
500 1.1 chris /*
501 1.1 chris * If the curproc on entry to cpu_switch was zero then the
502 1.1 chris * process that called it was exiting. This means that we do
503 1.1 chris * not need to save the current context. Instead we can jump
504 1.1 chris * straight to restoring the context for the new process.
505 1.1 chris */
506 1.9 thorpej teq r0, #0x00000000
507 1.14 briggs beq .Lswitch_exited
508 1.1 chris
509 1.9 thorpej /* rem: r0 = old proc */
510 1.1 chris /* rem: r6 = new process */
511 1.4 chris /* rem: interrupts are enabled */
512 1.1 chris
513 1.1 chris /* Stage two : Save old context */
514 1.1 chris
515 1.1 chris /* Get the user structure for the old process. */
516 1.9 thorpej ldr r1, [r0, #(P_ADDR)]
517 1.1 chris
518 1.1 chris /* Save all the registers in the old process's pcb */
519 1.1 chris add r7, r1, #(PCB_R8)
520 1.1 chris stmia r7, {r8-r13}
521 1.1 chris
522 1.1 chris /*
523 1.1 chris * This can be optimised... We know we want to go from SVC32
524 1.1 chris * mode to UND32 mode
525 1.1 chris */
526 1.13 thorpej mrs r3, cpsr
527 1.1 chris bic r2, r3, #(PSR_MODE)
528 1.1 chris orr r2, r2, #(PSR_UND32_MODE | I32_bit)
529 1.13 thorpej msr cpsr_c, r2
530 1.1 chris
531 1.1 chris str sp, [r1, #(PCB_UND_SP)]
532 1.1 chris
533 1.13 thorpej msr cpsr_c, r3 /* Restore the old mode */
534 1.1 chris
535 1.1 chris /* rem: r0 = old proc */
536 1.4 chris /* rem: r1 = old pcb */
537 1.1 chris /* rem: r6 = new process */
538 1.4 chris /* rem: interrupts are enabled */
539 1.1 chris
540 1.1 chris /* What else needs to be saved Only FPA stuff when that is supported */
541 1.1 chris
542 1.9 thorpej /* r1 now free! */
543 1.9 thorpej
544 1.1 chris /* Third phase : restore saved context */
545 1.1 chris
546 1.9 thorpej /* rem: r0 = old proc */
547 1.9 thorpej /* rem: r6 = new process */
548 1.9 thorpej /* rem: interrupts are enabled */
549 1.9 thorpej
550 1.9 thorpej /*
551 1.9 thorpej * Don't allow user space access between the purge and the switch.
552 1.9 thorpej */
553 1.17 thorpej ldr r3, .Lblock_userspace_access
554 1.9 thorpej mov r1, #0x00000001
555 1.9 thorpej mov r2, #0x00000000
556 1.9 thorpej str r1, [r3]
557 1.1 chris
558 1.1 chris stmfd sp!, {r0-r3}
559 1.17 thorpej ldr r1, .Lcpufuncs
560 1.23 bjh21 mov lr, pc
561 1.9 thorpej ldr pc, [r1, #CF_IDCACHE_WBINV_ALL]
562 1.1 chris ldmfd sp!, {r0-r3}
563 1.1 chris
564 1.14 briggs .Lcs_cache_purge_skipped:
565 1.1 chris /* At this point we need to kill IRQ's again. */
566 1.1 chris IRQdisable
567 1.1 chris
568 1.9 thorpej /*
569 1.9 thorpej * Interrupts are disabled so we can allow user space accesses again
570 1.1 chris * as none will occur until interrupts are re-enabled after the
571 1.1 chris * switch.
572 1.1 chris */
573 1.1 chris str r2, [r3]
574 1.1 chris
575 1.1 chris /* Get the user structure for the new process in r1 */
576 1.1 chris ldr r1, [r6, #(P_ADDR)]
577 1.1 chris
578 1.1 chris /* Get the pagedir physical address for the process. */
579 1.1 chris ldr r0, [r1, #(PCB_PAGEDIR)]
580 1.1 chris
581 1.1 chris /* Switch the memory to the new process */
582 1.17 thorpej ldr r3, .Lcpufuncs
583 1.23 bjh21 mov lr, pc
584 1.1 chris ldr pc, [r3, #CF_CONTEXT_SWITCH]
585 1.1 chris
586 1.1 chris /*
587 1.1 chris * This can be optimised... We know we want to go from SVC32
588 1.1 chris * mode to UND32 mode
589 1.1 chris */
590 1.13 thorpej mrs r3, cpsr
591 1.1 chris bic r2, r3, #(PSR_MODE)
592 1.1 chris orr r2, r2, #(PSR_UND32_MODE)
593 1.13 thorpej msr cpsr_c, r2
594 1.1 chris
595 1.1 chris ldr sp, [r1, #(PCB_UND_SP)]
596 1.1 chris
597 1.13 thorpej msr cpsr_c, r3 /* Restore the old mode */
598 1.1 chris
599 1.1 chris /* Restore all the save registers */
600 1.1 chris add r7, r1, #PCB_R8
601 1.1 chris ldmia r7, {r8-r13}
602 1.1 chris
603 1.18 thorpej mov r7, r1 /* preserve PCB pointer */
604 1.18 thorpej
605 1.1 chris #ifdef ARMFPE
606 1.1 chris add r0, r1, #(USER_SIZE) & 0x00ff
607 1.1 chris add r0, r0, #(USER_SIZE) & 0xff00
608 1.1 chris bl _C_LABEL(arm_fpe_core_changecontext)
609 1.1 chris #endif
610 1.1 chris
611 1.1 chris /* We can enable interrupts again */
612 1.1 chris IRQenable
613 1.1 chris
614 1.18 thorpej /* rem: r6 = new proc */
615 1.18 thorpej /* rem: r7 = new PCB */
616 1.18 thorpej
617 1.18 thorpej /*
618 1.18 thorpej * Check for restartable atomic sequences (RAS).
619 1.18 thorpej */
620 1.18 thorpej
621 1.18 thorpej ldr r2, [r6, #(P_NRAS)]
622 1.18 thorpej ldr r4, [r7, #(PCB_TF)] /* r4 = trapframe (used below) */
623 1.18 thorpej teq r2, #0 /* p->p_nras == 0? */
624 1.18 thorpej bne .Lswitch_do_ras /* no, check for one */
625 1.18 thorpej
626 1.14 briggs .Lswitch_return:
627 1.1 chris
628 1.1 chris /* Get the spl level from the stack and update the current spl level */
629 1.1 chris ldr r0, [sp], #0x0004
630 1.1 chris bl _C_LABEL(splx)
631 1.1 chris
632 1.1 chris /* cpu_switch returns the proc it switched to. */
633 1.1 chris mov r0, r6
634 1.1 chris
635 1.1 chris /*
636 1.1 chris * Pull the registers that got pushed when either savectx() or
637 1.1 chris * cpu_switch() was called and return.
638 1.1 chris */
639 1.1 chris ldmfd sp!, {r4-r7, pc}
640 1.18 thorpej
641 1.18 thorpej .Lswitch_do_ras:
642 1.18 thorpej ldr r1, [r4, #(TF_PC)] /* second ras_lookup() arg */
643 1.18 thorpej mov r0, r6 /* first ras_lookup() arg */
644 1.18 thorpej bl _C_LABEL(ras_lookup)
645 1.18 thorpej cmn r0, #1 /* -1 means "not in a RAS" */
646 1.18 thorpej strne r0, [r4, #(TF_PC)]
647 1.18 thorpej b .Lswitch_return
648 1.1 chris
649 1.14 briggs .Lswitch_exited:
650 1.9 thorpej /*
651 1.9 thorpej * We skip the cache purge because switch_exit() already did
652 1.9 thorpej * it. Load up registers the way Lcs_cache_purge_skipped
653 1.9 thorpej * expects. Userspace access already blocked in switch_exit().
654 1.9 thorpej */
655 1.17 thorpej ldr r3, .Lblock_userspace_access
656 1.9 thorpej mov r2, #0x00000000
657 1.14 briggs b .Lcs_cache_purge_skipped
658 1.9 thorpej
659 1.7 chris /*
660 1.8 thorpej * void switch_exit(struct proc *p, struct proc *p0);
661 1.7 chris * Switch to proc0's saved context and deallocate the address space and kernel
662 1.7 chris * stack for p. Then jump into cpu_switch(), as if we were in proc0 all along.
663 1.7 chris */
664 1.1 chris
665 1.8 thorpej /* LINTSTUB: Func: void switch_exit(struct proc *p, struct proc *p0) */
666 1.1 chris ENTRY(switch_exit)
667 1.1 chris /*
668 1.1 chris * r0 = proc
669 1.1 chris * r1 = proc0
670 1.1 chris */
671 1.1 chris
672 1.1 chris mov r3, r0
673 1.1 chris
674 1.1 chris /* In case we fault */
675 1.17 thorpej ldr r0, .Lcurproc
676 1.1 chris mov r2, #0x00000000
677 1.1 chris str r2, [r0]
678 1.1 chris
679 1.17 thorpej /* ldr r0, .Lcurpcb
680 1.1 chris str r2, [r0]*/
681 1.9 thorpej
682 1.9 thorpej /*
683 1.9 thorpej * Don't allow user space access between the purge and the switch.
684 1.9 thorpej */
685 1.17 thorpej ldr r0, .Lblock_userspace_access
686 1.9 thorpej mov r2, #0x00000001
687 1.9 thorpej str r2, [r0]
688 1.1 chris
689 1.1 chris /* Switch to proc0 context */
690 1.1 chris
691 1.1 chris stmfd sp!, {r0-r3}
692 1.1 chris
693 1.17 thorpej ldr r0, .Lcpufuncs
694 1.23 bjh21 mov lr, pc
695 1.6 thorpej ldr pc, [r0, #CF_IDCACHE_WBINV_ALL]
696 1.1 chris
697 1.1 chris ldmfd sp!, {r0-r3}
698 1.1 chris
699 1.1 chris IRQdisable
700 1.1 chris
701 1.1 chris ldr r2, [r1, #(P_ADDR)]
702 1.1 chris ldr r0, [r2, #(PCB_PAGEDIR)]
703 1.1 chris
704 1.1 chris /* Switch the memory to the new process */
705 1.17 thorpej ldr r4, .Lcpufuncs
706 1.23 bjh21 mov lr, pc
707 1.1 chris ldr pc, [r4, #CF_CONTEXT_SWITCH]
708 1.1 chris
709 1.1 chris /* Restore all the save registers */
710 1.1 chris add r7, r2, #PCB_R8
711 1.1 chris ldmia r7, {r8-r13}
712 1.1 chris
713 1.1 chris /* This is not really needed ! */
714 1.1 chris /* Yes it is for the su and fu routines */
715 1.17 thorpej ldr r0, .Lcurpcb
716 1.1 chris str r2, [r0]
717 1.1 chris
718 1.1 chris IRQenable
719 1.1 chris
720 1.1 chris /* str r3, [sp, #-0x0004]!*/
721 1.1 chris
722 1.1 chris /*
723 1.1 chris * Schedule the vmspace and stack to be freed.
724 1.1 chris */
725 1.1 chris mov r0, r3 /* exit2(p) */
726 1.1 chris bl _C_LABEL(exit2)
727 1.1 chris
728 1.1 chris /* Paranoia */
729 1.17 thorpej ldr r1, .Lcurproc
730 1.1 chris mov r0, #0x00000000
731 1.1 chris str r0, [r1]
732 1.1 chris
733 1.17 thorpej ldr r7, .Lwhichqs /* r7 = &whichqs */
734 1.8 thorpej mov r5, #0x00000000 /* r5 = old proc = NULL */
735 1.14 briggs b .Lswitch_search
736 1.1 chris
737 1.7 chris /* LINTSTUB: Func: void savectx(struct pcb *pcb) */
738 1.1 chris ENTRY(savectx)
739 1.1 chris /*
740 1.1 chris * r0 = pcb
741 1.1 chris */
742 1.1 chris
743 1.1 chris /* Push registers.*/
744 1.1 chris stmfd sp!, {r4-r7, lr}
745 1.1 chris
746 1.1 chris /* Store all the registers in the process's pcb */
747 1.1 chris add r2, r0, #(PCB_R8)
748 1.1 chris stmia r2, {r8-r13}
749 1.1 chris
750 1.1 chris /* Pull the regs of the stack */
751 1.1 chris ldmfd sp!, {r4-r7, pc}
752 1.1 chris
753 1.1 chris ENTRY(proc_trampoline)
754 1.19 bjh21 #ifdef MULTIPROCESSOR
755 1.19 bjh21 bl _C_LABEL(proc_trampoline_mp)
756 1.19 bjh21 #endif
757 1.1 chris mov r0, r5
758 1.1 chris mov r1, sp
759 1.24 bjh21 mov lr, pc
760 1.1 chris mov pc, r4
761 1.1 chris
762 1.1 chris /* Kill irq's */
763 1.13 thorpej mrs r0, cpsr
764 1.1 chris orr r0, r0, #(I32_bit)
765 1.13 thorpej msr cpsr_c, r0
766 1.1 chris
767 1.1 chris PULLFRAME
768 1.1 chris
769 1.1 chris movs pc, lr /* Exit */
770 1.1 chris
771 1.17 thorpej .type .Lcpu_switch_ffs_table, _ASM_TYPE_OBJECT;
772 1.17 thorpej .Lcpu_switch_ffs_table:
773 1.1 chris /* same as ffs table but all nums are -1 from that */
774 1.1 chris /* 0 1 2 3 4 5 6 7 */
775 1.1 chris .byte 0, 0, 1, 12, 2, 6, 0, 13 /* 0- 7 */
776 1.1 chris .byte 3, 0, 7, 0, 0, 0, 0, 14 /* 8-15 */
777 1.1 chris .byte 10, 4, 0, 0, 8, 0, 0, 25 /* 16-23 */
778 1.1 chris .byte 0, 0, 0, 0, 0, 21, 27, 15 /* 24-31 */
779 1.1 chris .byte 31, 11, 5, 0, 0, 0, 0, 0 /* 32-39 */
780 1.1 chris .byte 9, 0, 0, 24, 0, 0, 20, 26 /* 40-47 */
781 1.1 chris .byte 30, 0, 0, 0, 0, 23, 0, 19 /* 48-55 */
782 1.1 chris .byte 29, 0, 22, 18, 28, 17, 16, 0 /* 56-63 */
783 1.1 chris
784 1.1 chris /* End of cpuswitch.S */
785