cpuswitch.S revision 1.27 1 1.27 bjh21 /* $NetBSD: cpuswitch.S,v 1.27 2002/10/18 23:06:33 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 .text
74 1.1 chris
75 1.17 thorpej .Lwhichqs:
76 1.1 chris .word _C_LABEL(sched_whichqs)
77 1.1 chris
78 1.17 thorpej .Lqs:
79 1.1 chris .word _C_LABEL(sched_qs)
80 1.1 chris
81 1.1 chris /*
82 1.1 chris * cpuswitch()
83 1.1 chris *
84 1.1 chris * preforms a process context switch.
85 1.1 chris * This function has several entry points
86 1.1 chris */
87 1.1 chris
88 1.19 bjh21 #ifdef MULTIPROCESSOR
89 1.19 bjh21 .Lcpu_info_store:
90 1.19 bjh21 .word _C_LABEL(cpu_info_store)
91 1.19 bjh21 .Lcurproc:
92 1.19 bjh21 /* FIXME: This is bogus in the general case. */
93 1.19 bjh21 .word _C_LABEL(cpu_info_store) + CI_CURPROC
94 1.22 bjh21
95 1.22 bjh21 .Lcurpcb:
96 1.22 bjh21 .word _C_LABEL(cpu_info_store) + CI_CURPCB
97 1.19 bjh21 #else
98 1.17 thorpej .Lcurproc:
99 1.1 chris .word _C_LABEL(curproc)
100 1.1 chris
101 1.17 thorpej .Lcurpcb:
102 1.1 chris .word _C_LABEL(curpcb)
103 1.22 bjh21 #endif
104 1.1 chris
105 1.17 thorpej .Lwant_resched:
106 1.1 chris .word _C_LABEL(want_resched)
107 1.1 chris
108 1.17 thorpej .Lcpufuncs:
109 1.1 chris .word _C_LABEL(cpufuncs)
110 1.1 chris
111 1.22 bjh21 #ifndef MULTIPROCESSOR
112 1.1 chris .data
113 1.1 chris .global _C_LABEL(curpcb)
114 1.1 chris _C_LABEL(curpcb):
115 1.1 chris .word 0x00000000
116 1.1 chris .text
117 1.22 bjh21 #endif
118 1.1 chris
119 1.17 thorpej .Lblock_userspace_access:
120 1.1 chris .word _C_LABEL(block_userspace_access)
121 1.1 chris
122 1.15 thorpej .Lcpu_do_powersave:
123 1.15 thorpej .word _C_LABEL(cpu_do_powersave)
124 1.15 thorpej
125 1.1 chris /*
126 1.1 chris * Idle loop, exercised while waiting for a process to wake up.
127 1.16 thorpej *
128 1.16 thorpej * NOTE: When we jump back to .Lswitch_search, we must have a
129 1.16 thorpej * pointer to whichqs in r7, which is what it is when we arrive
130 1.16 thorpej * here.
131 1.1 chris */
132 1.7 chris /* LINTSTUB: Ignore */
133 1.4 chris ASENTRY_NP(idle)
134 1.19 bjh21 #if defined(MULTIPROCESSOR) || defined(LOCKDEBUG)
135 1.7 chris bl _C_LABEL(sched_unlock_idle)
136 1.7 chris #endif
137 1.16 thorpej ldr r3, .Lcpu_do_powersave
138 1.16 thorpej
139 1.1 chris /* Enable interrupts */
140 1.1 chris IRQenable
141 1.1 chris
142 1.15 thorpej /* If we don't want to sleep, use a simpler loop. */
143 1.16 thorpej ldr r3, [r3] /* r3 = cpu_do_powersave */
144 1.15 thorpej teq r3, #0
145 1.16 thorpej bne 2f
146 1.16 thorpej
147 1.16 thorpej /* Non-powersave idle. */
148 1.16 thorpej 1: /* should maybe do uvm pageidlezero stuff here */
149 1.16 thorpej ldr r3, [r7] /* r3 = whichqs */
150 1.16 thorpej teq r3, #0x00000000
151 1.16 thorpej bne .Lswitch_search
152 1.16 thorpej b 1b
153 1.15 thorpej
154 1.16 thorpej 2: /* Powersave idle. */
155 1.17 thorpej ldr r4, .Lcpufuncs
156 1.16 thorpej 3: ldr r3, [r7] /* r3 = whichqs */
157 1.15 thorpej teq r3, #0x00000000
158 1.15 thorpej bne .Lswitch_search
159 1.15 thorpej
160 1.15 thorpej /* if saving power, don't want to pageidlezero */
161 1.1 chris mov r0, #0
162 1.21 bjh21 adr lr, 3b
163 1.15 thorpej ldr pc, [r4, #(CF_SLEEP)]
164 1.15 thorpej /* loops back around */
165 1.15 thorpej
166 1.1 chris
167 1.1 chris /*
168 1.1 chris * Find a new process to run, save the current context and
169 1.1 chris * load the new context
170 1.1 chris */
171 1.1 chris
172 1.1 chris ENTRY(cpu_switch)
173 1.1 chris /*
174 1.1 chris * Local register usage. Some of these registers are out of date.
175 1.27 bjh21 * r0-r2 = scratch
176 1.1 chris * r3 = whichqs
177 1.1 chris * r4 = queue
178 1.1 chris * r5 = &qs[queue]
179 1.1 chris * r6 = newproc
180 1.27 bjh21 * r7 = &whichqs, new first proc in q, old pcb, new pcb
181 1.27 bjh21 * r8 = oldproc
182 1.1 chris */
183 1.26 bjh21 mov ip, sp
184 1.26 bjh21 stmfd sp!, {r4-r10, fp, ip, lr, pc}
185 1.26 bjh21 sub fp, ip, #4
186 1.1 chris
187 1.1 chris /*
188 1.1 chris * Get the current process and indicate that there is no longer
189 1.11 thorpej * a valid process (curproc = 0). Zero the current PCB pointer
190 1.11 thorpej * while we're at it.
191 1.1 chris */
192 1.27 bjh21 ldr r1, .Lcurproc
193 1.27 bjh21 ldr r2, .Lcurpcb
194 1.1 chris mov r0, #0x00000000
195 1.27 bjh21 ldr r8, [r1] /* r8 = curproc */
196 1.27 bjh21 str r0, [r1] /* curproc = NULL */
197 1.27 bjh21 str r0, [r2] /* curpcb = NULL */
198 1.1 chris
199 1.19 bjh21 #if defined(MULTIPROCESSOR) || defined(LOCKDEBUG)
200 1.7 chris /* release the sched_lock before handling interrupts */
201 1.7 chris bl _C_LABEL(sched_unlock_idle)
202 1.7 chris #endif
203 1.7 chris
204 1.7 chris /* Lower the spl level to spl0 and get the current spl level. */
205 1.5 thorpej #ifdef __NEWINTR
206 1.5 thorpej mov r0, #(IPL_NONE)
207 1.5 thorpej bl _C_LABEL(_spllower)
208 1.5 thorpej #else /* ! __NEWINTR */
209 1.1 chris #ifdef spl0
210 1.1 chris mov r0, #(_SPL_0)
211 1.1 chris bl _C_LABEL(splx)
212 1.1 chris #else
213 1.1 chris bl _C_LABEL(spl0)
214 1.5 thorpej #endif /* spl0 */
215 1.5 thorpej #endif /* __NEWINTR */
216 1.1 chris
217 1.1 chris /* Push the old spl level onto the stack */
218 1.1 chris str r0, [sp, #-0x0004]!
219 1.1 chris
220 1.1 chris /* First phase : find a new process */
221 1.1 chris
222 1.17 thorpej ldr r7, .Lwhichqs
223 1.16 thorpej
224 1.16 thorpej /* rem: r7 = &whichqs */
225 1.27 bjh21 /* rem: r8 = old proc */
226 1.7 chris
227 1.14 briggs .Lswitch_search:
228 1.1 chris IRQdisable
229 1.19 bjh21 #if defined(MULTIPROCESSOR) || defined(LOCKDEBUG)
230 1.7 chris bl _C_LABEL(sched_lock_idle)
231 1.7 chris #endif
232 1.7 chris
233 1.1 chris /* Do we have any active queues */
234 1.1 chris ldr r3, [r7]
235 1.1 chris
236 1.1 chris /* If not we must idle until we do. */
237 1.1 chris teq r3, #0x00000000
238 1.4 chris beq _ASM_LABEL(idle)
239 1.7 chris
240 1.27 bjh21 /* rem: r8 = old proc */
241 1.1 chris /* rem: r3 = whichqs */
242 1.1 chris /* rem: interrupts are disabled */
243 1.1 chris
244 1.1 chris /*
245 1.1 chris * We have found an active queue. Currently we do not know which queue
246 1.1 chris * is active just that one of them is.
247 1.1 chris */
248 1.1 chris /* this is the ffs algorithm devised by d.seal and posted to
249 1.1 chris * comp.sys.arm on 16 Feb 1994.
250 1.1 chris */
251 1.1 chris rsb r5, r3, #0
252 1.1 chris ands r0, r3, r5
253 1.1 chris
254 1.17 thorpej adr r5, .Lcpu_switch_ffs_table
255 1.1 chris
256 1.3 chris /* X = R0 */
257 1.3 chris orr r4, r0, r0, lsl #4 /* r4 = X * 0x11 */
258 1.3 chris orr r4, r4, r4, lsl #6 /* r4 = X * 0x451 */
259 1.3 chris rsb r4, r4, r4, lsl #16 /* r4 = X * 0x0450fbaf */
260 1.1 chris
261 1.1 chris /* used further down, saves SA stall */
262 1.17 thorpej ldr r6, .Lqs
263 1.1 chris
264 1.3 chris /* now lookup in table indexed on top 6 bits of a4 */
265 1.1 chris ldrb r4, [ r5, r4, lsr #26 ]
266 1.1 chris
267 1.1 chris /* rem: r0 = bit mask of chosen queue (1 << r4) */
268 1.1 chris /* rem: r3 = whichqs */
269 1.1 chris /* rem: r4 = queue number */
270 1.27 bjh21 /* rem: r8 = old proc */
271 1.1 chris /* rem: interrupts are disabled */
272 1.1 chris
273 1.1 chris /* Get the address of the queue (&qs[queue]) */
274 1.1 chris add r5, r6, r4, lsl #3
275 1.1 chris
276 1.1 chris /*
277 1.1 chris * Get the process from the queue and place the next process in
278 1.1 chris * the queue at the head. This basically unlinks the process at
279 1.1 chris * the head of the queue.
280 1.1 chris */
281 1.1 chris ldr r6, [r5, #(P_FORW)]
282 1.1 chris
283 1.1 chris /* rem: r6 = new process */
284 1.1 chris ldr r7, [r6, #(P_FORW)]
285 1.1 chris str r7, [r5, #(P_FORW)]
286 1.1 chris
287 1.27 bjh21 /* rem: r7 = new queue head */
288 1.27 bjh21
289 1.1 chris /*
290 1.1 chris * Test to see if the queue is now empty. If the head of the queue
291 1.1 chris * points to the queue itself then there are no more processes in
292 1.1 chris * the queue. We can therefore clear the queue not empty flag held
293 1.1 chris * in r3.
294 1.1 chris */
295 1.1 chris
296 1.1 chris teq r5, r7
297 1.1 chris biceq r3, r3, r0
298 1.1 chris
299 1.1 chris /* Fix the back pointer for the process now at the head of the queue. */
300 1.1 chris ldr r0, [r6, #(P_BACK)]
301 1.1 chris str r0, [r7, #(P_BACK)]
302 1.1 chris
303 1.1 chris /* Update the RAM copy of the queue not empty flags word. */
304 1.17 thorpej ldr r7, .Lwhichqs
305 1.1 chris str r3, [r7]
306 1.1 chris
307 1.27 bjh21 /* rem: r8 = old proc */
308 1.1 chris /* rem: r3 = whichqs - NOT NEEDED ANY MORE */
309 1.1 chris /* rem: r4 = queue number - NOT NEEDED ANY MORE */
310 1.1 chris /* rem: r6 = new process */
311 1.1 chris /* rem: interrupts are disabled */
312 1.1 chris
313 1.1 chris /* Clear the want_resched flag */
314 1.27 bjh21 ldr r1, .Lwant_resched
315 1.1 chris mov r0, #0x00000000
316 1.27 bjh21 str r0, [r1]
317 1.1 chris
318 1.1 chris /*
319 1.1 chris * Clear the back pointer of the process we have removed from
320 1.1 chris * the head of the queue. The new process is isolated now.
321 1.1 chris */
322 1.1 chris str r0, [r6, #(P_BACK)]
323 1.1 chris
324 1.19 bjh21 #if defined(MULTIPROCESSOR) || defined(LOCKDEBUG)
325 1.7 chris /*
326 1.7 chris * unlock the sched_lock, but leave interrupts off, for now.
327 1.7 chris */
328 1.7 chris bl _C_LABEL(sched_unlock_idle)
329 1.7 chris #endif
330 1.7 chris
331 1.19 bjh21 #ifdef MULTIPROCESSOR
332 1.19 bjh21 /* XXX use curcpu() */
333 1.19 bjh21 ldr r0, .Lcpu_info_store
334 1.19 bjh21 str r0, [r6, #(P_CPU)]
335 1.19 bjh21 #else
336 1.1 chris /* p->p_cpu initialized in fork1() for single-processor */
337 1.19 bjh21 #endif
338 1.1 chris
339 1.1 chris /* Process is now on a processor. */
340 1.1 chris mov r0, #SONPROC /* p->p_stat = SONPROC */
341 1.1 chris strb r0, [r6, #(P_STAT)]
342 1.1 chris
343 1.1 chris /* We have a new curproc now so make a note it */
344 1.17 thorpej ldr r7, .Lcurproc
345 1.1 chris str r6, [r7]
346 1.1 chris
347 1.1 chris /* Hook in a new pcb */
348 1.17 thorpej ldr r7, .Lcurpcb
349 1.1 chris ldr r0, [r6, #(P_ADDR)]
350 1.1 chris str r0, [r7]
351 1.1 chris
352 1.1 chris /* At this point we can allow IRQ's again. */
353 1.1 chris IRQenable
354 1.1 chris
355 1.27 bjh21 /* rem: r8 = old proc */
356 1.1 chris /* rem: r6 = new process */
357 1.4 chris /* rem: interrupts are enabled */
358 1.1 chris
359 1.1 chris /*
360 1.1 chris * If the new process is the same as the process that called
361 1.1 chris * cpu_switch() then we do not need to save and restore any
362 1.1 chris * contexts. This means we can make a quick exit.
363 1.27 bjh21 * The test is simple if curproc on entry (now in r8) is the
364 1.1 chris * same as the proc removed from the queue we can jump to the exit.
365 1.1 chris */
366 1.27 bjh21 teq r8, r6
367 1.14 briggs beq .Lswitch_return
368 1.1 chris
369 1.1 chris /*
370 1.1 chris * If the curproc on entry to cpu_switch was zero then the
371 1.1 chris * process that called it was exiting. This means that we do
372 1.1 chris * not need to save the current context. Instead we can jump
373 1.1 chris * straight to restoring the context for the new process.
374 1.1 chris */
375 1.27 bjh21 teq r8, #0x00000000
376 1.14 briggs beq .Lswitch_exited
377 1.1 chris
378 1.27 bjh21 /* rem: r8 = old proc */
379 1.1 chris /* rem: r6 = new process */
380 1.4 chris /* rem: interrupts are enabled */
381 1.1 chris
382 1.1 chris /* Stage two : Save old context */
383 1.1 chris
384 1.1 chris /* Get the user structure for the old process. */
385 1.27 bjh21 ldr r7, [r8, #(P_ADDR)]
386 1.1 chris
387 1.26 bjh21 /* Save the remaining registers in the old process's pcb */
388 1.27 bjh21 add r0, r7, #(PCB_R11)
389 1.27 bjh21 stmia r0, {r11-r13}
390 1.27 bjh21
391 1.27 bjh21 /* rem: r7 = old pcb */
392 1.1 chris
393 1.1 chris /*
394 1.1 chris * This can be optimised... We know we want to go from SVC32
395 1.1 chris * mode to UND32 mode
396 1.1 chris */
397 1.13 thorpej mrs r3, cpsr
398 1.1 chris bic r2, r3, #(PSR_MODE)
399 1.1 chris orr r2, r2, #(PSR_UND32_MODE | I32_bit)
400 1.13 thorpej msr cpsr_c, r2
401 1.1 chris
402 1.27 bjh21 str sp, [r7, #(PCB_UND_SP)]
403 1.1 chris
404 1.13 thorpej msr cpsr_c, r3 /* Restore the old mode */
405 1.1 chris
406 1.1 chris /* rem: r6 = new process */
407 1.27 bjh21 /* rem: r7 = old pcb */
408 1.27 bjh21 /* rem: r8 = old proc */
409 1.4 chris /* rem: interrupts are enabled */
410 1.1 chris
411 1.1 chris /* What else needs to be saved Only FPA stuff when that is supported */
412 1.1 chris
413 1.27 bjh21 /* r7 now free! */
414 1.9 thorpej
415 1.1 chris /* Third phase : restore saved context */
416 1.1 chris
417 1.9 thorpej /* rem: r6 = new process */
418 1.27 bjh21 /* rem: r8 = old proc */
419 1.9 thorpej /* rem: interrupts are enabled */
420 1.9 thorpej
421 1.9 thorpej /*
422 1.9 thorpej * Don't allow user space access between the purge and the switch.
423 1.9 thorpej */
424 1.17 thorpej ldr r3, .Lblock_userspace_access
425 1.9 thorpej mov r1, #0x00000001
426 1.9 thorpej mov r2, #0x00000000
427 1.9 thorpej str r1, [r3]
428 1.1 chris
429 1.1 chris stmfd sp!, {r0-r3}
430 1.17 thorpej ldr r1, .Lcpufuncs
431 1.23 bjh21 mov lr, pc
432 1.9 thorpej ldr pc, [r1, #CF_IDCACHE_WBINV_ALL]
433 1.1 chris ldmfd sp!, {r0-r3}
434 1.1 chris
435 1.14 briggs .Lcs_cache_purge_skipped:
436 1.1 chris /* At this point we need to kill IRQ's again. */
437 1.1 chris IRQdisable
438 1.1 chris
439 1.9 thorpej /*
440 1.9 thorpej * Interrupts are disabled so we can allow user space accesses again
441 1.1 chris * as none will occur until interrupts are re-enabled after the
442 1.1 chris * switch.
443 1.1 chris */
444 1.1 chris str r2, [r3]
445 1.1 chris
446 1.1 chris /* Get the user structure for the new process in r1 */
447 1.27 bjh21 ldr r7, [r6, #(P_ADDR)]
448 1.1 chris
449 1.1 chris /* Get the pagedir physical address for the process. */
450 1.27 bjh21 ldr r0, [r7, #(PCB_PAGEDIR)]
451 1.1 chris
452 1.1 chris /* Switch the memory to the new process */
453 1.17 thorpej ldr r3, .Lcpufuncs
454 1.23 bjh21 mov lr, pc
455 1.1 chris ldr pc, [r3, #CF_CONTEXT_SWITCH]
456 1.1 chris
457 1.1 chris /*
458 1.1 chris * This can be optimised... We know we want to go from SVC32
459 1.1 chris * mode to UND32 mode
460 1.1 chris */
461 1.13 thorpej mrs r3, cpsr
462 1.1 chris bic r2, r3, #(PSR_MODE)
463 1.1 chris orr r2, r2, #(PSR_UND32_MODE)
464 1.13 thorpej msr cpsr_c, r2
465 1.1 chris
466 1.27 bjh21 ldr sp, [r7, #(PCB_UND_SP)]
467 1.1 chris
468 1.13 thorpej msr cpsr_c, r3 /* Restore the old mode */
469 1.1 chris
470 1.26 bjh21 /* Restore the saved registers from the PCB */
471 1.27 bjh21 add r0, r7, #PCB_R11
472 1.27 bjh21 ldmia r0, {r11-r13}
473 1.18 thorpej
474 1.1 chris #ifdef ARMFPE
475 1.1 chris add r0, r1, #(USER_SIZE) & 0x00ff
476 1.1 chris add r0, r0, #(USER_SIZE) & 0xff00
477 1.1 chris bl _C_LABEL(arm_fpe_core_changecontext)
478 1.1 chris #endif
479 1.1 chris
480 1.1 chris /* We can enable interrupts again */
481 1.1 chris IRQenable
482 1.1 chris
483 1.18 thorpej /* rem: r6 = new proc */
484 1.18 thorpej /* rem: r7 = new PCB */
485 1.18 thorpej
486 1.18 thorpej /*
487 1.18 thorpej * Check for restartable atomic sequences (RAS).
488 1.18 thorpej */
489 1.18 thorpej
490 1.18 thorpej ldr r2, [r6, #(P_NRAS)]
491 1.18 thorpej ldr r4, [r7, #(PCB_TF)] /* r4 = trapframe (used below) */
492 1.18 thorpej teq r2, #0 /* p->p_nras == 0? */
493 1.18 thorpej bne .Lswitch_do_ras /* no, check for one */
494 1.18 thorpej
495 1.14 briggs .Lswitch_return:
496 1.1 chris
497 1.1 chris /* Get the spl level from the stack and update the current spl level */
498 1.1 chris ldr r0, [sp], #0x0004
499 1.1 chris bl _C_LABEL(splx)
500 1.1 chris
501 1.1 chris /* cpu_switch returns the proc it switched to. */
502 1.1 chris mov r0, r6
503 1.1 chris
504 1.1 chris /*
505 1.1 chris * Pull the registers that got pushed when either savectx() or
506 1.1 chris * cpu_switch() was called and return.
507 1.1 chris */
508 1.26 bjh21 ldmdb fp, {r4-r10, fp, sp, pc}
509 1.18 thorpej
510 1.18 thorpej .Lswitch_do_ras:
511 1.18 thorpej ldr r1, [r4, #(TF_PC)] /* second ras_lookup() arg */
512 1.18 thorpej mov r0, r6 /* first ras_lookup() arg */
513 1.18 thorpej bl _C_LABEL(ras_lookup)
514 1.18 thorpej cmn r0, #1 /* -1 means "not in a RAS" */
515 1.18 thorpej strne r0, [r4, #(TF_PC)]
516 1.18 thorpej b .Lswitch_return
517 1.1 chris
518 1.14 briggs .Lswitch_exited:
519 1.9 thorpej /*
520 1.9 thorpej * We skip the cache purge because switch_exit() already did
521 1.9 thorpej * it. Load up registers the way Lcs_cache_purge_skipped
522 1.9 thorpej * expects. Userspace access already blocked in switch_exit().
523 1.9 thorpej */
524 1.17 thorpej ldr r3, .Lblock_userspace_access
525 1.9 thorpej mov r2, #0x00000000
526 1.14 briggs b .Lcs_cache_purge_skipped
527 1.9 thorpej
528 1.7 chris /*
529 1.8 thorpej * void switch_exit(struct proc *p, struct proc *p0);
530 1.7 chris * Switch to proc0's saved context and deallocate the address space and kernel
531 1.7 chris * stack for p. Then jump into cpu_switch(), as if we were in proc0 all along.
532 1.7 chris */
533 1.1 chris
534 1.8 thorpej /* LINTSTUB: Func: void switch_exit(struct proc *p, struct proc *p0) */
535 1.1 chris ENTRY(switch_exit)
536 1.1 chris /*
537 1.1 chris * r0 = proc
538 1.1 chris * r1 = proc0
539 1.1 chris */
540 1.1 chris
541 1.1 chris mov r3, r0
542 1.1 chris
543 1.1 chris /* In case we fault */
544 1.17 thorpej ldr r0, .Lcurproc
545 1.1 chris mov r2, #0x00000000
546 1.1 chris str r2, [r0]
547 1.1 chris
548 1.17 thorpej /* ldr r0, .Lcurpcb
549 1.1 chris str r2, [r0]*/
550 1.9 thorpej
551 1.9 thorpej /*
552 1.9 thorpej * Don't allow user space access between the purge and the switch.
553 1.9 thorpej */
554 1.17 thorpej ldr r0, .Lblock_userspace_access
555 1.9 thorpej mov r2, #0x00000001
556 1.9 thorpej str r2, [r0]
557 1.1 chris
558 1.1 chris /* Switch to proc0 context */
559 1.1 chris
560 1.1 chris stmfd sp!, {r0-r3}
561 1.1 chris
562 1.17 thorpej ldr r0, .Lcpufuncs
563 1.23 bjh21 mov lr, pc
564 1.6 thorpej ldr pc, [r0, #CF_IDCACHE_WBINV_ALL]
565 1.1 chris
566 1.1 chris ldmfd sp!, {r0-r3}
567 1.1 chris
568 1.1 chris IRQdisable
569 1.1 chris
570 1.1 chris ldr r2, [r1, #(P_ADDR)]
571 1.1 chris ldr r0, [r2, #(PCB_PAGEDIR)]
572 1.1 chris
573 1.1 chris /* Switch the memory to the new process */
574 1.17 thorpej ldr r4, .Lcpufuncs
575 1.23 bjh21 mov lr, pc
576 1.1 chris ldr pc, [r4, #CF_CONTEXT_SWITCH]
577 1.1 chris
578 1.1 chris /* Restore all the save registers */
579 1.26 bjh21 add r7, r2, #PCB_R11
580 1.26 bjh21 ldmia r7, {r11-r13}
581 1.1 chris
582 1.1 chris /* This is not really needed ! */
583 1.1 chris /* Yes it is for the su and fu routines */
584 1.17 thorpej ldr r0, .Lcurpcb
585 1.1 chris str r2, [r0]
586 1.1 chris
587 1.1 chris IRQenable
588 1.1 chris
589 1.1 chris /* str r3, [sp, #-0x0004]!*/
590 1.1 chris
591 1.1 chris /*
592 1.1 chris * Schedule the vmspace and stack to be freed.
593 1.1 chris */
594 1.1 chris mov r0, r3 /* exit2(p) */
595 1.1 chris bl _C_LABEL(exit2)
596 1.1 chris
597 1.1 chris /* Paranoia */
598 1.17 thorpej ldr r1, .Lcurproc
599 1.1 chris mov r0, #0x00000000
600 1.1 chris str r0, [r1]
601 1.1 chris
602 1.17 thorpej ldr r7, .Lwhichqs /* r7 = &whichqs */
603 1.27 bjh21 mov r8, #0x00000000 /* r8 = old proc = NULL */
604 1.14 briggs b .Lswitch_search
605 1.1 chris
606 1.7 chris /* LINTSTUB: Func: void savectx(struct pcb *pcb) */
607 1.1 chris ENTRY(savectx)
608 1.1 chris /*
609 1.1 chris * r0 = pcb
610 1.1 chris */
611 1.1 chris
612 1.1 chris /* Push registers.*/
613 1.26 bjh21 mov ip, sp
614 1.26 bjh21 stmfd sp!, {r4-r10, fp, ip, lr, pc}
615 1.26 bjh21 sub fp, ip, #4
616 1.1 chris
617 1.1 chris /* Store all the registers in the process's pcb */
618 1.26 bjh21 add r2, r0, #(PCB_R11)
619 1.26 bjh21 stmia r2, {r11-r13}
620 1.1 chris
621 1.1 chris /* Pull the regs of the stack */
622 1.26 bjh21 ldmdb fp, {r4-r10, fp, sp, pc}
623 1.1 chris
624 1.1 chris ENTRY(proc_trampoline)
625 1.19 bjh21 #ifdef MULTIPROCESSOR
626 1.19 bjh21 bl _C_LABEL(proc_trampoline_mp)
627 1.19 bjh21 #endif
628 1.1 chris mov r0, r5
629 1.1 chris mov r1, sp
630 1.24 bjh21 mov lr, pc
631 1.1 chris mov pc, r4
632 1.1 chris
633 1.1 chris /* Kill irq's */
634 1.13 thorpej mrs r0, cpsr
635 1.1 chris orr r0, r0, #(I32_bit)
636 1.13 thorpej msr cpsr_c, r0
637 1.1 chris
638 1.1 chris PULLFRAME
639 1.1 chris
640 1.1 chris movs pc, lr /* Exit */
641 1.1 chris
642 1.17 thorpej .type .Lcpu_switch_ffs_table, _ASM_TYPE_OBJECT;
643 1.17 thorpej .Lcpu_switch_ffs_table:
644 1.1 chris /* same as ffs table but all nums are -1 from that */
645 1.1 chris /* 0 1 2 3 4 5 6 7 */
646 1.1 chris .byte 0, 0, 1, 12, 2, 6, 0, 13 /* 0- 7 */
647 1.1 chris .byte 3, 0, 7, 0, 0, 0, 0, 14 /* 8-15 */
648 1.1 chris .byte 10, 4, 0, 0, 8, 0, 0, 25 /* 16-23 */
649 1.1 chris .byte 0, 0, 0, 0, 0, 21, 27, 15 /* 24-31 */
650 1.1 chris .byte 31, 11, 5, 0, 0, 0, 0, 0 /* 32-39 */
651 1.1 chris .byte 9, 0, 0, 24, 0, 0, 20, 26 /* 40-47 */
652 1.1 chris .byte 30, 0, 0, 0, 0, 23, 0, 19 /* 48-55 */
653 1.1 chris .byte 29, 0, 22, 18, 28, 17, 16, 0 /* 56-63 */
654 1.1 chris
655 1.1 chris /* End of cpuswitch.S */
656