1/*  *********************************************************************
2    *  Broadcom Common Firmware Environment (CFE)
3    *
4    *  CPU init module				File: sb1250_altcpu.S
5    *
6    *  Secondary core startup routines for CFE
7    *
8    *  Author:  Mitch Lichtenberg
9    *
10    *********************************************************************
11    *
12    *  Copyright 2000,2001,2002,2003
13    *  Broadcom Corporation. All rights reserved.
14    *
15    *  This software is furnished under license and may be used and
16    *  copied only in accordance with the following terms and
17    *  conditions.  Subject to these conditions, you may download,
18    *  copy, install, use, modify and distribute modified or unmodified
19    *  copies of this software in source and/or binary form.  No title
20    *  or ownership is transferred hereby.
21    *
22    *  1) Any source code used, modified or distributed must reproduce
23    *     and retain this copyright notice and list of conditions
24    *     as they appear in the source file.
25    *
26    *  2) No right is granted to use any trade name, trademark, or
27    *     logo of Broadcom Corporation.  The "Broadcom Corporation"
28    *     name may not be used to endorse or promote products derived
29    *     from this software without the prior written permission of
30    *     Broadcom Corporation.
31    *
32    *  3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
33    *     IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
34    *     WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
35    *     PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
36    *     SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
37    *     PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
38    *     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
39    *     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
40    *     GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
41    *     BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
42    *     OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
43    *     TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
44    *     THE POSSIBILITY OF SUCH DAMAGE.
45    ********************************************************************* */
46
47#include "sbmips.h"
48#include "exception.h"
49
50#include "bsp_config.h"
51
52#ifdef _CFE_
53#include "cfe_devfuncs.h"
54#else
55#define CFE_EPTSEAL 0x43464531
56#endif
57
58#include "sb1250_defs.h"
59#include "sb1250_regs.h"
60#include "sb1250_scd.h"
61
62#include "cpu_config.h"
63
64/*  *********************************************************************
65    *  Macros
66    ********************************************************************* */
67
68#include "mipsmacros.h"
69
70#define SETLEDS1(a,b,c,d)                     \
71       li     a0,(((a)<<24)|((b)<<16)|((c)<<8)|(d)) ;    \
72       JAL_KSEG1(board_setleds)
73#define SETLEDS(a,b,c,d)                     \
74       li     a0,(((a)<<24)|((b)<<16)|((c)<<8)|(d)) ;    \
75       JAL(board_setleds)
76
77
78/*  *********************************************************************
79    *  Initialized Data
80    ********************************************************************* */
81
82                .sdata
83
84/*
85 * Initial start addresses for secondary CPUs
86 */
87
88		.globl cpu_startvectors
89cpu_startvectors:
90		.dword	0			# cpu #0 (not used)
91		.dword	0			# cpu #1
92
93/*
94 * Initial values for SP, GP, and A1 (user argument)
95 */
96
97cpu_start_spvals:
98		.dword	0			# cpu #0 (not used)
99		.dword	0			# cpu #1
100
101cpu_start_gpvals:
102		.dword	0			# cpu #0 (not used)
103		.dword	0			# cpu #1
104
105cpu_start_args:
106		.dword	0			# cpu #0 (not used)
107		.dword	0			# cpu #1
108
109
110		.extern mem_datareloc
111
112
113/*  *********************************************************************
114    *  Linkage Tables
115    *
116    *  This table contains pointers to routines in other modules.
117    *  we do things this way so we can stay position-independent and
118    *  also avoid problems with the limitations of relative branching.
119    ********************************************************************* */
120
121		.text
122
123		.set mips64
124
125/*  *********************************************************************
126    *  ALTCPU_KSEG1_SWITCH
127    *
128    *  Hack the return address so we will come back in KSEG1 (uncached)
129    *
130    *  Input parameters:
131    *  	   nothing
132    *
133    *  Return value:
134    *  	   nothing
135    ********************************************************************* */
136
137altcpu_kseg1_switch:
138
139		and	ra,(K0SIZE-1)
140		or	ra,K1BASE
141		jr	ra
142
143
144/*  *********************************************************************
145    *  ALTCPU_KSEG0_SWITCH
146    *
147    *  Hack the return address so we will come back in KSEG0
148    *
149    *  Input parameters:
150    *  	   nothing
151    *
152    *  Return value:
153    *  	   nothing
154    ********************************************************************* */
155
156altcpu_kseg0_switch:
157
158		and	ra,(K0SIZE-1)
159		or	ra,K0BASE
160		jr	ra
161
162
163/*  *********************************************************************
164    *  SB1250_ALTCPU_START1
165    *
166    *  Start secondary processor(s).  These processors will start
167    *  running the code at ALTCPU_RESET (see below).  We wait here
168    *  for the secondary processor(s) to finish their cache
169    *  initialization and then  return.
170    *
171    *  This routine is normally run from KSEG1
172    *
173    *  Input parameters:
174    *  	   nothing
175    *
176    *  Return value:
177    *  	   nothing
178    ********************************************************************* */
179
180LEAF(sb1250_altcpu_start1)
181
182	/*
183	 * Don't do this if we have only one CPU.  This way we can
184	 * support running the multiprocessor version of CFE
185	 * with only one core.
186	 */
187
188sb1250_altcpu_start1a:
189
190		la	t0,PHYS_TO_K1(A_SCD_SYSTEM_REVISION)
191		ld	t0,(t0)			# Get system revision
192		dsrl	t0,S_SYS_PART		# Shift part # to low bits
193		dsrl	t0,8			# isolate CPU part of number
194		and	t0,0x0F			# T0 = number of CPUs
195		bgt	t0,1,1f			# Keep going if more than one CPU
196		j	ra			# Go back home, nothing to do
1971:
198
199	/*
200	 * Clear out our mailbox registers (both CPUs)
201	 */
202
203		la	a0,PHYS_TO_K1(A_IMR_REGISTER(0,R_IMR_MAILBOX_CLR_CPU))
204		dli	t0,-1			# clear all 64 bits
205		sd	t0,(a0)
206		la	a0,PHYS_TO_K1(A_IMR_REGISTER(1,R_IMR_MAILBOX_CLR_CPU))
207		sd	t0,(a0)
208
209	/*
210	 * Let the secondary CPU(s) out of reset
211	 *
212	 * XXX This is very SB1250-specific at the moment.
213	 */
214
215		la	a0,PHYS_TO_K1(A_SCD_SYSTEM_CFG)
216		ld	t0,0(a0)
217		dli	t1,M_SYS_CPU_RESET_1	# Reset mask
218		not	t1			# clear this bit
219		and	t0,t1			# New value to write
220		sd	t0,0(a0)		# CPU1 is now running
221
222	/*
223	 * Wait for the other CPU to ring our doorbell
224	 */
225
226
2271:		la	a0,PHYS_TO_K1(A_IMR_REGISTER(0,R_IMR_MAILBOX_CPU));
228		ld	t0,(a0)			# Read mailbox
229		beq	t0,zero,1b		# Loop till the bit is set
230
231	/*
232	 * Clear the mailbox to dismiss the pending interrupts
233	 */
234
235		la	a0,PHYS_TO_K1(A_IMR_REGISTER(0,R_IMR_MAILBOX_CLR_CPU))
236		dli	t0,-1			# clear all 64 bits
237		sd	t0,(a0)
238
239	/*
240	 * Okay, it's safe to return
241	 */
242
243		j	ra
244
245
246END(sb1250_altcpu_start1)
247
248/*  *********************************************************************
249    *  SB1250_ALTCPU_START2
250    *
251    *  Finish startup of secondary processor(s) - we pass the relocation
252    *  offset to the other CPUs here, and the CPUs relocate their
253    *  data segments and go to the idle loop.
254    *
255    *  This routine is normally run from KSEG0
256    *
257    *  Input parameters:
258    *  	   a0 - data relocation offset (0=none)
259    *
260    *  Return value:
261    *  	   nothing
262    ********************************************************************* */
263
264LEAF(sb1250_altcpu_start2)
265
266	/*
267	 * Don't do this if we have only one CPU.
268	 */
269
270		la	t0,PHYS_TO_K1(A_SCD_SYSTEM_REVISION)
271		ld	t0,(t0)			# Get system revision
272		dsrl	t0,S_SYS_PART		# Shift part # to low bits
273		dsrl	t0,8			# isolate CPU part of number
274		and	t0,0x0F			# T0 = number of CPUs
275		bgt	t0,1,1f			# Keep going if more than one CPU
276		j	ra			# Go back home, nothing to do
2771:
278
279	/*
280	 * Let secondary CPU(s) run their idle loops.  Set the
281	 * mailbox register to our relocation factor so we can read
282	 * it out of the mailbox register and relocate GP properly.
283	 */
284
285		la	t1,PHYS_TO_K1(A_IMR_REGISTER(1,R_IMR_MAILBOX_SET_CPU))
286		or	t0,a0,1		# hack - make sure reloc is nonzero
287		sd	t0,0(t1)	# Write to mailbox register
288
289		j	ra
290
291END(sb1250_altcpu_start2)
292
293/*  *********************************************************************
294    *  SB1250_ALTCPU_KILL
295    *
296    *  Kill a secondary CPU, causing it to return to the idle
297    *  loop.  We do this by switching to uncached mode,
298    *  asserting RESET on the other CPU, and then re-run
299    *  ALTCPU_START again.
300    *
301    *  Input parameters:
302    *  	   nothing
303    *
304    *  Return value:
305    *  	   nothing
306    ********************************************************************* */
307
308LEAF(sb1250_altcpu_kill)
309
310	/*
311	 * Don't do this if we have only one CPU.
312	 */
313
314		la	t0,PHYS_TO_K1(A_SCD_SYSTEM_REVISION)
315		ld	t0,(t0)			# Get system revision
316		dsrl	t0,S_SYS_PART		# Shift part # to low bits
317		dsrl	t0,8			# isolate CPU part of number
318		and	t0,0x0F			# T0 = number of CPUs
319		bgt	t0,1,1f			# Keep going if more than one CPU
320		j	ra			# Go back home, nothing to do
3211:
322
323	/*
324	 * More than one CPU, go ahead...
325	 */
326
327		move	t7,ra			# save RA, we'll make calls
328
329#ifdef _SB1250_PASS1_WORKAROUNDS_
330       #
331       # Not sure what we need to do here wrt cacheability of
332       # the genbus space, if anything.  Some portion of CPU1's
333       # istream will come from L1, but the data should all be from
334       # DRAM.  These references will be cacheable noncoherent,
335       # should we worry if cpu0 is coherent shared at this time?
336       # probably.
337       #
338#endif
339
340		la	t1,cpu_startvectors
341		sd	zero,8(t1)		# Reset address of CPU1 (2nd entry in table)
342
343	#
344	# Flush the D cache to ensure that the write above made it
345	# out of our L1.
346	#
347
348		JAL(sb1250_l1cache_flush_d)	# uses t0, t2, t3
349
350	#
351	# Switch to KSEG1 to quiesce our cache activity.
352	#
353
354		bal	altcpu_kseg1_switch	# switch to uncached mode
355
356	#
357	# Force CPU1 into reset
358	#
359
360		li	a0,PHYS_TO_K1(A_SCD_SYSTEM_CFG)
361		ld	t0,0(a0)
362		dli	t1,M_SYS_CPU_RESET_1	# Reset mask
363		or	t0,t1			# New value to write
364		sd	t0,0(a0)		# CPU1 is now in reset
365
366	#
367	# Not sure how long we're supposed to wait.
368	#
369		ssnop
370		ssnop
371		ssnop
372		ssnop
373
374	#
375	# Now restart CPU1
376	#
377
378		bal	sb1250_altcpu_start1a
379
380	#
381	# It's safe to be cached again.
382	#
383
384		bal	altcpu_kseg0_switch
385
386	#
387	# At this point, CPU1 is waiting for us to indicate that it's
388	# okay to use memory again.  Ring its doorbell.
389	#
390
391		la	a0,PHYS_TO_K1(A_IMR_REGISTER(1,R_IMR_MAILBOX_SET_CPU))
392		LR	t0,mem_datareloc
393		or	t0,1
394		sd	t0,0(a0)
395
396	#
397	# CPU1 is back in our control.
398	#
399
400		move	ra,t7
401		move	v0,zero
402
403		j	ra
404
405
406END(sb1250_altcpu_kill)
407
408
409/*  *********************************************************************
410    *  ALTCPU_RESET
411    *
412    *  Start address for secondary CPU(s) - do the initialization of
413    *  the local CPU and then notify CPU0 that we're done.
414    *
415    *  This routine is called in KSEG1.
416    *
417    *  Input parameters:
418    *  	   t0 - CPU identifier
419    *
420    *  Return value:
421    *  	   nothing
422    ********************************************************************* */
423
424
425LEAF(sb1250_altcpu_reset)
426
427
428		mfc0	t0,C0_PRID		# get CPU PRID register
429		and	t0,t0,0xe000000		# determine cpu number
430		beq	t0,zero,iscpu0		# go if  on CPU0
431
432
433#if CFG_RELOC
434       /*
435        * SVR4 PIC mode: get a copy of GP for use in the boot ROM.
436	*/
437		lui	gp,%hi(_gp)
438		addiu	gp,%lo(_gp)
439		or	gp,gp,K1BASE
440#endif
441
442	/*
443	 * Note: we should never get to the CPU1 code if we're
444	 * with only one CPU.  Theoretically, nobody got past the
445	 * check in altcpu_start.  But, just in case, if we
446	 * get here and we're on CPU1, and we supposedly only
447	 * have one CPU, reset CPU1.
448	 */
449
450		la	t0,PHYS_TO_K1(A_SCD_SYSTEM_REVISION)
451		ld	t0,(t0)			# Get system revision
452		dsrl	t0,S_SYS_PART		# Shift part # to low bits
453		dsrl	t0,8			# isolate CPU part of number
454		and	t0,0x0F			# T0 = number of CPUs
455		beq	t0,1,iscpu0		# If only one CPU, kill off CPU1
456
457
458	/*
459	 * Initialize CPU registers.
460	 */
461
462		JAL_KSEG1(sb1_cpu_init)
463
464#ifdef _SB1250_PASS1_WORKAROUNDS_
465	/*
466	 * See the above note about this workaround.  It's important
467	 * to remain noncoherent until we're done with the flash.
468	 */
469
470		SETCCAMODE(v0,K_CFG_K0COH_CACHEABLE) /* cacheable NONCOHERENT */
471#endif
472
473		SETLEDS1('C','P','U','1')
474
475	/*
476	 * Initialize the L1 cache
477	 */
478
479#if CFG_INIT_L1
480		JAL_KSEG1(sb1250_l1cache_init)
481#endif
482
483
484	/*
485	 * Notify the SCD that we're done initializing.  Do this by
486	 * ringing CPU0's doorbell.
487	 */
488
489		la	a0,PHYS_TO_K1(A_IMR_REGISTER(0,R_IMR_MAILBOX_SET_CPU));
490
491		mfc0	t0,C0_PRID		# get processor number
492		srl	t0,t0,25		# shift CPU bits into low
493		and	t0,t0,7			# keep only low 3 bits
494		li	t1,1			# make a bit mask depending on CPU
495		sll	t1,t1,t0		# calculate t1 = 1 shl cpu number
496		sd	t1,0(a0)		# set corresponding bit in mailbox
497
498
499	 /*
500	  * Go to the idle loop
501	  */
502
503		b	altcpu_idle		# go to idle loop
504
505
506   	 /*
507	  * We get here if we were running on CPU0.  Make things
508	  * pretty for the reset of CPU initialization.
509	  */
510
511
512iscpu0:
513
514	/*
515	 * If we are on CPU0, then force CPU1 into reset.  This is needed
516	 * for the case where the firmware has crashed and we need to get
517	 * control of the system again.
518	 */
519
520		li	a0,PHYS_TO_K1(A_SCD_SYSTEM_CFG)
521		ld	t0,0(a0)
522		dli	t1,M_SYS_CPU_RESET_1	# Reset mask
523		or	t0,t1			# New value to write
524		sd	t0,0(a0)		# CPU1 is now in reset
525
526		j	ra			# return (we were on CPU 0)
527
528END(sb1250_altcpu_reset)
529
530
531/*  *********************************************************************
532    *  ALTCPU_CMD_START(cpu,addr)
533    *
534    *  Start an alternate CPU.
535    *
536    *  Input parameters:
537    *  	   a0 - cpu number (must be 1 for the SB1250)
538    *  	   a1 - pointer to start parameters (four 64-bit values)
539    *             array[0] = start address (PC)
540    *             array[1] = start stack pointer (SP)
541    *             array[2] = start global pointer (GP)
542    *             array[3] = start user argument (A1)
543    *
544    *  Return value:
545    *  	   v0 - 0 if ok
546    *  	   else -1 if request could not be handled
547    ********************************************************************* */
548
549#define R_CPUSTART_PCVAL  0
550#define R_CPUSTART_SPVAL  8
551#define R_CPUSTART_GPVAL  16
552#define R_CPUSTART_A1VAL  24
553
554LEAF(altcpu_cmd_start)
555
556		li	v0,-1		/* assume failure */
557		bne	a0,1,1f		/* go if not CPU 1 */
558
559	/*
560	 * Return an error if running in uniprocessor mode.
561	 */
562
563		la	t0,PHYS_TO_K1(A_SCD_SYSTEM_REVISION)
564		ld	t0,(t0)			# Get system revision
565		dsrl	t0,S_SYS_PART		# Shift part # to low bits
566		dsrl	t0,8			# isolate CPU part of number
567		and	t0,0x0F			# T0 = number of CPUs
568		beq	t0,1,1f			# If only one CPU, error.
569
570	/*
571	 * Multiprocessor mode, start the other CPU
572	 */
573
574		move	t0,a0		/* get CPU number */
575		sll	t0,3		/* multiply by 8 for table index */
576
577		la	t1,cpu_start_gpvals
578		add	t1,t0		/* copy the GP value */
579		ld	t2,R_CPUSTART_GPVAL(a1)
580		sd	t2,0(t1)
581
582		la	t1,cpu_start_spvals
583		add	t1,t0		/* copy the SP value */
584		ld	t2,R_CPUSTART_SPVAL(a1)
585		sd	t2,0(t1)
586
587		la	t1,cpu_start_args
588		add	t1,t0		/* copy the A1 value */
589		ld	t2,R_CPUSTART_A1VAL(a1)
590		sd	t2,0(t1)
591
592		la	t1,cpu_startvectors
593		add	t1,t0		/* copy the PC value */
594		ld	t2,R_CPUSTART_PCVAL(a1)
595		sd	t2,0(t1)
596
597		move	v0,zero		/* success */
598
5991:		j	ra
600
601END(altcpu_cmd_start)
602
603/*  *********************************************************************
604    *  ALTCPU_CMD_STOP(cpu)
605    *
606    *  Stop the specified CPU.
607    *
608    *  We don't really support this at the moment.
609    *
610    *  Input parameters:
611    *  	   a0 - cpu number
612    *
613    *  Return value:
614    *  	   v0 - 0 if ok, else error code
615    ********************************************************************* */
616
617LEAF(altcpu_cmd_stop)
618
619		li	v0,-1		/* assume failure */
620		bne	a0,1,1f		/* go if not CPU 1 */
621
622	/*
623	 * Return an error if running in uniprocessor mode.
624	 */
625
626		la	t0,PHYS_TO_K1(A_SCD_SYSTEM_REVISION)
627		ld	t0,(t0)			# Get system revision
628		dsrl	t0,S_SYS_PART		# Shift part # to low bits
629		dsrl	t0,8			# isolate CPU part of number
630		and	t0,0x0F			# T0 = number of CPUs
631		beq	t0,1,1f			# If only one CPU, error.
632
633	/*
634	 * Multiprocessor mode, stop the other CPU
635	 */
636
637		JMP(sb1250_altcpu_kill)	/* kill the CPU */
638
6391:		j	ra
640
641END(altcpu_cmd_stop)
642
643/*  *********************************************************************
644    *  ALTCPU_IDLE
645    *
646    *  Loop forever waiting for someone to tell us where to go.
647    *
648    *  Input parameters:
649    *  	   nothing.
650    *
651    *  Return value:
652    *  	   nothing
653    ********************************************************************* */
654
655altcpu_idle:
656
657	/*
658	 * Switch to KSEG0 (cached)
659	 */
660
661		bal	altcpu_kseg0_switch
662
663#if CFG_RELOC
664		lui	gp,%hi(_gp)
665		addiu	gp,%lo(_gp)
666#endif
667
668		SETLEDS('c','p','u','1')
669
670
671	/*
672	 * Now wait for CPU0 to ring *our* doorbell.  This is our signal that
673	 * it's safe to go to the idle loop.  Until CPU0 rings our
674	 * doorbell, we can't use memory (but we can use the cache).
675	 * XXX Very SB1250 specific here.
676	 */
677
6781:		la	a0,PHYS_TO_K1(A_IMR_REGISTER(1,R_IMR_MAILBOX_CPU))
679		ld	t0,(a0)			# Read mailbox
680		beq	t0,zero,1b		# Loop till the bit is set
681
682	/*
683	 * Clear all the bits in the mailbox register to dismiss the
684	 * pending interrupt
685	 */
686
687		la	a0,PHYS_TO_K1(A_IMR_REGISTER(1,R_IMR_MAILBOX_CLR_CPU))
688		li	t1,-1
689		sd	t1,0(a0)
690
691
692	/*
693	 * We may need GP, especially in relocated version
694	 *
695	 * Yucky hack: The relocation factor was passed to us in
696	 * the mailbox register, which is conveniently in t0 right now.
697	 * (except the lower bit is set just in case the reloc was
698	 * zero, so clear that first).
699	 */
700
701		li	t1,1			# 1
702		not	t1			# FFFFFFFFE
703		and	t0,t1			# clear lower bit.
704
705
706#if (CFG_RELOC)
707
708	/*
709	 * SVR4 PIC: GP is already set to GOT, relocate
710	 * it and jump to relocated code
711	 */
712__AltCpuGoRel:
713
714		la	t1,1f			# Get address of where to go
715		ADDU	gp,t0			# Relocate GP
716		ADDU	t1,t0			# Relocate address
717		jr	t1			# Go there.
718
7191:						# we will go "here" in the reloc world
720#else
721	 /*
722	  * non-PIC: Standard GP
723	  */
724		la	gp,_gp
725		ADD	gp,t0			# relocate GP.
726#endif
727
728
729	/*
730	 * Get our processor number
731	 */
732
733		mfc0	t0,C0_PRID		# Get PRID (for processor id)
734
735		srl	t0,t0,25		# shift CPU bits into low
736		and	t0,t0,7			# keep only low 3 bits
737		sll	t0,t0,3			# multiply by 8 for table index
738
739	/*
740	 * Set up registers like we were launching a program.
741	 */
742
743		la	a2,cpu_apientry # A2 = firmware entry vector
744		move	a0,gp		# A0 = handle
745		li	a3,CFE_EPTSEAL  # A3 = entrypoint signature
746
747
748#ifdef _SB1250_PASS1_WORKAROUNDS_
749	/*
750	 * Okay, it's safe now to be coherent.
751	 * Flush the D cache to invalidate all the lines we have,
752	 * then change the config register back.
753	 *
754	 * Danger! It's imperative that *no stores to memory* be done
755	 * prior to this point, otherwise flushing the cache
756	 * will race with core 0, which will also be flushing
757	 * lines at this time.
758	 */
759		move	k0,t0
760		JAL(sb1250_l1cache_flush_d)
761		SETCCAMODE(v0,K_CFG_K0COH_COHERENT) /* cacheable coherent */
762		move    t0,k0
763#endif
764
765	/*
766	 * Read the start address from the CPU restart table
767	 * and jump to it.  For an idle CPU, the address in the
768	 * table below will be the zero, causing
769	 * the CPU to loop forever.  To start a secondary CPU,
770	 * just write an address in cpu_startvectors[cpu_id]
771	 *
772	 * Warning: This kind of assumes that this code will
773	 * live in cacheable space.  If it doesn't, it will
774	 * probably cause lots of unwanted bus traffic.
775	 */
776		li	s4,0
777
778loop_forever:	ld	t1,cpu_startvectors(t0) # Load address of routine
779		beq	t1,zero,loop_forever
780		ld	a1,cpu_start_args(t0)	# Load user argument (A1)
781		ld	sp,cpu_start_spvals(t0)	# Load stack pointer
782		ld	t2,cpu_start_gpvals(t0) # Load global pointer
783		move	gp,t2			# and put in real register
784		j	t1			# jump to start address
785
786
787/*  *********************************************************************
788    *  End
789    ********************************************************************* */
790