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