2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 * @OSF_FREE_COPYRIGHT@
29 * @APPLE_FREE_COPYRIGHT@
35 General interface to the MP hardware handlers anonymous
37 Lovingly crafted by Bill Angell using traditional methods and only natural or recycled materials.
38 No animal products are used other than rendered otter bile.
44 #include <ppc/proc_reg.h>
45 #include <ppc/POWERMAC/mp/MPPlugIn.h>
46 #include <mach/machine/vm_param.h>
50 * This first section is the glue for the high level C code.
51 * Anything that needs any kind of system services (e.g., VM) has to be done here. The firmware
52 * code that implements the SC runs in real mode.
57 /* #define MPI_DEBUGGING 0 */
58 #define MPI_DEBUGGING 0
61 * The routine that implements cpu_number.
64 ENTRY(cpu_number, TAG_NO_FRAME_USED)
66 mfmsr r9 /* Save the old MSR */
67 rlwinm r8,r9,0,17,15 /* Clear interruptions */
68 mtmsr r8 /* Interrupts off */
69 mfsprg r7,0 /* Get per-proc block */
70 lhz r3,PP_CPU_NUMBER(r7) /* Get CPU number */
71 mtmsr r9 /* Restore interruptions to entry */
76 * The routine glues to the count CPU firmware call
79 ENTRY(MPgetProcCount, TAG_NO_FRAME_USED)
81 mr r12,r0 /* Keep R0 pristene */
82 lis r0,HIGH_ADDR(MPgetProcCountCall) /* Top half of MPgetProcCount firmware call number */
83 ori r0,r0,LOW_ADDR(MPgetProcCountCall) /* Bottom half */
84 sc /* Go see how many processors we have */
87 lis r0,HIGH_ADDR(CutTrace) /* Top half of trace entry maker call */
88 ori r0,r0,LOW_ADDR(CutTrace) /* Bottom half of trace entry maker call */
89 sc /* Cut a backend trace entry */
92 mr r0,r12 /* Restore R0 */
94 blr /* Return, pass back R3... */
97 * The routine glues to the start CPU firmware call - actually it's really a boot
98 * The first parameter is the CPU number to start
99 * The second parameter is the real address of the code used to boot the processor
100 * The third parameter is the real addess of the CSA for the subject processor
103 ENTRY(MPstart, TAG_NO_FRAME_USED)
105 mr r12,r0 /* Keep R0 pristene */
106 lis r0,HIGH_ADDR(MPstartCall) /* Top half of MPstartCall firmware call number */
107 ori r0,r0,LOW_ADDR(MPstartCall) /* Bottom half */
108 sc /* Go see how many processors we have */
111 lis r0,HIGH_ADDR(CutTrace) /* Top half of trace entry maker call */
112 ori r0,r0,LOW_ADDR(CutTrace) /* Bottom half of trace entry maker call */
113 sc /* Cut a backend trace entry */
116 mr r0,r12 /* Restore R0 */
120 * This routine glues to the get external interrupt handler physical address
123 ENTRY(MPexternalHook, TAG_NO_FRAME_USED)
125 mr r12,r0 /* Keep R0 pristene */
126 lis r0,HIGH_ADDR(MPexternalHookCall) /* Top half of MPexternalHookCall firmware call number */
127 ori r0,r0,LOW_ADDR(MPexternalHookCall) /* Bottom half */
128 sc /* Go see how many processors we have */
131 lis r0,HIGH_ADDR(CutTrace) /* Top half of trace entry maker call */
132 ori r0,r0,LOW_ADDR(CutTrace) /* Bottom half of trace entry maker call */
133 sc /* Cut a backend trace entry */
136 mr r0,r12 /* Restore R0 */
141 * This routine glues to the signal processor routine
144 ENTRY(MPsignal, TAG_NO_FRAME_USED)
146 mr r12,r0 /* Keep R0 pristene */
147 lis r0,HIGH_ADDR(MPsignalCall) /* Top half of MPsignalCall firmware call number */
148 ori r0,r0,LOW_ADDR(MPsignalCall) /* Bottom half */
149 sc /* Go kick the other guy */
152 lis r0,HIGH_ADDR(CutTrace) /* Top half of trace entry maker call */
153 ori r0,r0,LOW_ADDR(CutTrace) /* Bottom half of trace entry maker call */
154 sc /* Cut a backend trace entry */
157 mr r0,r12 /* Restore R0 */
162 * This routine glues to the stop processor routine
165 ENTRY(MPstop, TAG_NO_FRAME_USED)
167 mr r12,r0 /* Keep R0 pristene */
168 lis r0,HIGH_ADDR(MPstopCall) /* Top half of MPsignalCall firmware call number */
169 ori r0,r0,LOW_ADDR(MPstopCall) /* Bottom half */
170 sc /* Stop the other guy cold */
173 lis r0,HIGH_ADDR(CutTrace) /* Top half of trace entry maker call */
174 ori r0,r0,LOW_ADDR(CutTrace) /* Bottom half of trace entry maker call */
175 sc /* Cut a backend trace entry */
178 mr r0,r12 /* Restore R0 */
182 /* *************************************************************************************************************
184 * This second section is the glue for the low level stuff directly into the MP plugin.
185 * At this point every register in existence should be saved. Well, they're saved,
186 * but R13 points to the savearea, and R20 to the trace entry. Please be careful
187 * with these. You won't like what happens if they're different when you exit.
189 ***************************************************************************************************************/
193 * See how many physical processors we have
196 ENTRY(MPgetProcCountLL, TAG_NO_FRAME_USED)
198 lis r11,HIGH_ADDR(EXT(MPEntries)) /* Get the address of the MP entry block (in the V=R area) */
199 ori r11,r11,LOW_ADDR(EXT(MPEntries)) /* Get the bottom of the MP spec area */
200 lwz r10,kCountProcessors*4(r11) /* Get the routine entry point */
201 mflr r14 /* Save the return in an unused register */
202 mtlr r10 /* Set it */
203 blrl /* Call the routine */
204 mtlr r14 /* Restore firmware caller address */
208 * Start up a processor
211 ENTRY(MPstartLL, TAG_NO_FRAME_USED)
213 lis r11,HIGH_ADDR(EXT(MPEntries)) /* Get the address of the MP entry block (in the V=R area) */
214 ori r11,r11,LOW_ADDR(EXT(MPEntries)) /* Get the bottom of the MP spec area */
215 lwz r10,kStartProcessor*4(r11) /* Get the routine entry point */
216 mflr r14 /* Save the return in an unused register */
217 mtlr r10 /* Set it */
218 blrl /* Call the routine */
219 mtlr r14 /* Restore firmware caller address */
223 * Get physical address of SIGP external handler
226 ENTRY(MPexternalHookLL, TAG_NO_FRAME_USED)
228 lis r11,HIGH_ADDR(EXT(MPEntries)) /* Get the address of the MP entry block (in the V=R area) */
229 ori r11,r11,LOW_ADDR(EXT(MPEntries)) /* Get the bottom of the MP spec area */
230 lwz r10,kExternalHook*4(r11) /* Get the routine entry point */
231 mflr r14 /* Save the return in an unused register */
232 mtlr r10 /* Set it */
233 blrl /* Call the routine */
234 mtlr r14 /* Restore firmware caller address */
240 * Send a signal to another processor
243 ENTRY(MPsignalLL, TAG_NO_FRAME_USED)
245 lis r11,HIGH_ADDR(EXT(MPEntries)) /* Get the address of the MP entry block (in the V=R area) */
246 ori r11,r11,LOW_ADDR(EXT(MPEntries)) /* Get the bottom of the MP spec area */
247 lwz r10,kSignalProcessor*4(r11) /* Get the routine entry point */
248 mflr r14 /* Save the return in an unused register */
249 mtlr r10 /* Set it */
250 blrl /* Call the routine */
251 mtlr r14 /* Restore firmware caller address */
257 * Stop another processor
260 ENTRY(MPstopLL, TAG_NO_FRAME_USED)
262 lis r11,HIGH_ADDR(EXT(MPEntries)) /* Get the address of the MP entry block (in the V=R area) */
263 ori r11,r11,LOW_ADDR(EXT(MPEntries)) /* Get the bottom of the MP spec area */
264 lwz r10,kStopProcessor*4(r11) /* Get the routine entry point */
265 mflr r14 /* Save the return in an unused register */
266 mtlr r10 /* Set it */
267 blrl /* Call the routine */
268 mtlr r14 /* Restore firmware caller address */
273 * Third section: Miscellaneous MP related routines
279 * All non-primary CPUs start here.
280 * We are dispatched by the SMP driver. Addressing is real (no DR or IR),
281 * interruptions disabled, etc. R3 points to the CPUStatusArea (CSA) which contains
282 * most of the state for the processor. This is set up by the primary. Note that we
283 * do not use everything in the CSA. Caches should be clear and coherent with
284 * no paradoxies (well, maybe one doxie, a pair would be pushing it).
287 ENTRY(start_secondary,TAG_NO_FRAME_USED)
289 mr r31,r3 /* Get the pointer to the CSA */
291 lis r21,HIGH_ADDR(SpinTimeOut) /* Get the top part of the spin timeout */
292 ori r21,r21,LOW_ADDR(SpinTimeOut) /* Slam in the bottom part */
294 GetValid: lbz r10,CSAregsAreValid(r31) /* Get the CSA validity value */
297 mr. r10,r10 /* Is the area valid yet? */
298 bne GotValid /* Yeah... */
299 addic. r21,r21,-1 /* Count the try */
300 isync /* Make sure we don't prefetch the valid flag */
301 bge+ GetValid /* Still more tries left... */
302 blr /* Return and cancel startup request... */
304 GotValid: li r21,0 /* Set the valid flag off (the won't be after the RFI) */
305 lwz r10,CSAdec(r31) /* Get the decrimenter */
306 stb r21,CSAregsAreValid(r31) /* Clear that validity flag */
308 lwz r11,CSAdbat+(0*8)+0(r31) /* Get the first DBAT */
309 lwz r12,CSAdbat+(0*8)+4(r31) /* Get the first DBAT */
310 lwz r13,CSAdbat+(1*8)+0(r31) /* Get the second DBAT */
311 mtdec r10 /* Set the decrimenter */
312 lwz r14,CSAdbat+(1*8)+4(r31) /* Get the second DBAT */
313 mtdbatu 0,r11 /* Set top part of DBAT 0 */
314 lwz r15,CSAdbat+(2*8)+0(r31) /* Get the third DBAT */
315 mtdbatl 0,r12 /* Set lower part of DBAT 0 */
316 lwz r16,CSAdbat+(2*8)+4(r31) /* Get the third DBAT */
317 mtdbatu 1,r13 /* Set top part of DBAT 1 */
318 lwz r17,CSAdbat+(3*8)+0(r31) /* Get the fourth DBAT */
319 mtdbatl 1,r14 /* Set lower part of DBAT 1 */
320 lwz r18,CSAdbat+(3*8)+4(r31) /* Get the fourth DBAT */
321 mtdbatu 2,r15 /* Set top part of DBAT 2 */
322 lwz r11,CSAibat+(0*8)+0(r31) /* Get the first IBAT */
323 mtdbatl 2,r16 /* Set lower part of DBAT 2 */
324 lwz r12,CSAibat+(0*8)+4(r31) /* Get the first IBAT */
325 mtdbatu 3,r17 /* Set top part of DBAT 3 */
326 lwz r13,CSAibat+(1*8)+0(r31) /* Get the second IBAT */
327 mtdbatl 3,r18 /* Set lower part of DBAT 3 */
328 lwz r14,CSAibat+(1*8)+4(r31) /* Get the second IBAT */
329 mtibatu 0,r11 /* Set top part of IBAT 0 */
330 lwz r15,CSAibat+(2*8)+0(r31) /* Get the third IBAT */
331 mtibatl 0,r12 /* Set lower part of IBAT 0 */
332 lwz r16,CSAibat+(2*8)+4(r31) /* Get the third IBAT */
333 mtibatu 1,r13 /* Set top part of IBAT 1 */
334 lwz r17,CSAibat+(3*8)+0(r31) /* Get the fourth IBAT */
335 mtibatl 1,r14 /* Set lower part of IBAT 1 */
336 lwz r18,CSAibat+(3*8)+4(r31) /* Get the fourth IBAT */
337 mtibatu 2,r15 /* Set top part of IBAT 2 */
338 lwz r11,CSAsdr1(r31) /* Get the SDR1 value */
339 mtibatl 2,r16 /* Set lower part of IBAT 2 */
340 lwz r12,CSAsprg(r31) /* Get SPRG0 (the per_proc_info address) */
341 mtibatu 3,r17 /* Set top part of IBAT 3 */
342 lwz r13,CSAmsr(r31) /* Get the MSR */
343 mtibatl 3,r18 /* Set lower part of IBAT 3 */
344 lwz r14,CSApc(r31) /* Get the PC */
346 mtsdr1 r11 /* Set the SDR1 value */
349 la r10,CSAsr-4(r31) /* Point to SR 0 - 4 */
350 li r9,0 /* Start at SR 0 */
352 LoadSRs: lwz r8,4(r10) /* Get the next SR in line */
354 mtsrin r8,r9 /* Load up the SR */
355 addis r9,r9,0x1000 /* Bump to the next SR */
356 mr. r9,r9 /* See if we wrapped back to 0 */
357 bne+ LoadSRs /* Not yet... */
359 lwz r0,CSAgpr+(0*4)(r31) /* Get a GPR */
360 lwz r9,CSAsprg+(1*4)(r31) /* Get SPRG1 (the initial active savearea) */
361 mtsrr1 r13 /* Set the MSR to dispatch */
362 lwz r1,CSAgpr+(1*4)(r31) /* Get a GPR */
363 mtsprg 0,r12 /* Set the SPRG0 (per_proc_into) value */
364 lwz r2,CSAgpr+(2*4)(r31) /* Get a GPR */
365 mtsrr0 r14 /* Set the PC to dispatch */
366 lwz r3,CSAgpr+(3*4)(r31) /* Get a GPR */
367 mtsprg 1,r9 /* Set the SPRG1 (the initial active savearea) value */
368 lwz r4,CSAgpr+(4*4)(r31) /* Get a GPR */
369 lwz r5,CSAgpr+(5*4)(r31) /* Get a GPR */
370 lwz r6,CSAgpr+(6*4)(r31) /* Get a GPR */
371 lwz r7,CSAgpr+(7*4)(r31) /* Get a GPR */
372 lwz r8,CSAgpr+(8*4)(r31) /* Get a GPR */
373 lwz r9,CSAgpr+(9*4)(r31) /* Get a GPR */
374 lwz r10,CSAgpr+(10*4)(r31) /* Get a GPR */
375 lwz r11,CSAgpr+(11*4)(r31) /* Get a GPR */
376 lwz r12,CSAgpr+(12*4)(r31) /* Get a GPR */
377 lwz r13,CSAgpr+(13*4)(r31) /* Get a GPR */
378 lwz r14,CSAgpr+(14*4)(r31) /* Get a GPR */
379 lwz r15,CSAgpr+(15*4)(r31) /* Get a GPR */
380 lwz r16,CSAgpr+(16*4)(r31) /* Get a GPR */
381 lwz r17,CSAgpr+(17*4)(r31) /* Get a GPR */
382 lwz r18,CSAgpr+(18*4)(r31) /* Get a GPR */
383 lwz r19,CSAgpr+(19*4)(r31) /* Get a GPR */
384 lwz r20,CSAgpr+(20*4)(r31) /* Get a GPR */
385 lwz r21,CSAgpr+(21*4)(r31) /* Get a GPR */
386 lwz r22,CSAgpr+(22*4)(r31) /* Get a GPR */
387 lwz r23,CSAgpr+(23*4)(r31) /* Get a GPR */
388 lwz r24,CSAgpr+(24*4)(r31) /* Get a GPR */
389 lwz r25,CSAgpr+(25*4)(r31) /* Get a GPR */
390 lwz r26,CSAgpr+(26*4)(r31) /* Get a GPR */
391 lwz r27,CSAgpr+(27*4)(r31) /* Get a GPR */
392 lwz r28,CSAgpr+(28*4)(r31) /* Get a GPR */
393 lwz r29,CSAgpr+(29*4)(r31) /* Get a GPR */
394 lwz r30,CSAgpr+(30*4)(r31) /* Get a GPR */
395 lwz r31,CSAgpr+(31*4)(r31) /* Get a GPR */
397 sync /* Make sure we're sunk */
399 rfi /* Get the whole shebang going... */
414 * This routine handles requests to firmware from another processor. It is actually the second level
415 * of a three level signaling protocol. The first level is handled in the physical MP driver. It is the
416 * basic physical control for the processor, e.g., physical stop, reset, start. The second level (this
417 * one) handles cross-processor firmware requests, e.g., complete TLB purges. The last are AST requests
418 * which are handled directly by mach.
420 * If this code handles the request (based upon MPPICParm0BU which is valid until the next SIGP happens -
421 * actually, don't count on it once you enable) it will RFI back to the
422 * interrupted code. If not, it will return and let the higher level interrupt handler be called.
424 * We need to worry about registers we use here, check in lowmem_vectors to see what is boten and verboten.
426 * Note that there are no functions implemented yet.
430 ENTRY(MPsignalFW, TAG_NO_FRAME_USED)
433 mfspr r7,pir /* Get the processor address */
434 lis r6,HIGH_ADDR(EXT(MPPICPUs)) /* Get high part of CPU control block array */
435 rlwinm r7,r7,5,23,26 /* Get index into CPU array */
436 ori r6,r6,HIGH_ADDR(EXT(MPPICPUs)) /* Get low part of CPU control block array */
437 add r7,r7,r6 /* Point to the control block for this processor */
438 lwz r6,MPPICParm0BU(r7) /* Just pick this up for now */
443 * Make space for the maximum supported CPUs in the data section
453 .set ., .+(CSAsize*NCPUS)
455 .type EXT(CSA), @object
456 .size EXT(CSA), CSAsize*NCPUS