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