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