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