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