1/*
2 * relocate_kernel.S for kexec
3 * Created by <nschichan@corp.free.fr> on Thu Oct 12 17:49:57 2006
4 *
5 * This source code is licensed under the GNU General Public License,
6 * Version 2.  See the file COPYING for more details.
7 */
8
9#include <asm/asm.h>
10#include <asm/asmmacro.h>
11#include <asm/regdef.h>
12#include <asm/mipsregs.h>
13#include <asm/stackframe.h>
14#include <asm/addrspace.h>
15
16LEAF(relocate_new_kernel)
17	PTR_L a0,	arg0
18	PTR_L a1,	arg1
19	PTR_L a2,	arg2
20	PTR_L a3,	arg3
21
22	PTR_L		s0, kexec_indirection_page
23	PTR_L		s1, kexec_start_address
24
25process_entry:
26	PTR_L		s2, (s0)
27	PTR_ADD		s0, s0, SZREG
28
29	/* destination page */
30	and		s3, s2, 0x1
31	beq		s3, zero, 1f
32	and		s4, s2, ~0x1	/* store destination addr in s4 */
33	b		process_entry
34
351:
36	/* indirection page, update s0  */
37	and		s3, s2, 0x2
38	beq		s3, zero, 1f
39	and		s0, s2, ~0x2
40	b		process_entry
41
421:
43	/* done page */
44	and		s3, s2, 0x4
45	beq		s3, zero, 1f
46	b		done
471:
48	/* source page */
49	and		s3, s2, 0x8
50	beq		s3, zero, process_entry
51	and		s2, s2, ~0x8
52	li		s6, (1 << _PAGE_SHIFT) / SZREG
53
54copy_word:
55	/* copy page word by word */
56	REG_L		s5, (s2)
57	REG_S		s5, (s4)
58	PTR_ADD		s4, s4, SZREG
59	PTR_ADD		s2, s2, SZREG
60	LONG_SUB	s6, s6, 1
61	beq		s6, zero, process_entry
62	b		copy_word
63	b		process_entry
64
65done:
66#ifdef CONFIG_SMP
67	/* kexec_flag reset is signal to other CPUs what kernel
68	   was moved to it's location. Note - we need relocated address
69	   of kexec_flag.  */
70
71	bal		1f
72 1: 	move		t1,ra;
73	PTR_LA		t2,1b
74	PTR_LA		t0,kexec_flag
75	PTR_SUB		t0,t0,t2;
76	PTR_ADD		t0,t1,t0;
77	LONG_S		zero,(t0)
78#endif
79
80#ifdef CONFIG_CPU_CAVIUM_OCTEON
81	/* We need to flush I-cache before jumping to new kernel.
82	 * Unfortunatelly, this code is cpu-specific.
83	 */
84	.set push
85	.set noreorder
86	syncw
87	syncw
88	synci		0($0)
89	.set pop
90#else
91	sync
92#endif
93	/* jump to kexec_start_address */
94	j		s1
95	END(relocate_new_kernel)
96
97#ifdef CONFIG_SMP
98/*
99 * Other CPUs should wait until code is relocated and
100 * then start at entry (?) point.
101 */
102LEAF(kexec_smp_wait)
103	PTR_L		a0, s_arg0
104	PTR_L		a1, s_arg1
105	PTR_L		a2, s_arg2
106	PTR_L		a3, s_arg3
107	PTR_L		s1, kexec_start_address
108
109	/* Non-relocated address works for args and kexec_start_address ( old
110	 * kernel is not overwritten). But we need relocated address of
111	 * kexec_flag.
112	 */
113
114	bal		1f
1151:	move		t1,ra;
116	PTR_LA		t2,1b
117	PTR_LA		t0,kexec_flag
118	PTR_SUB		t0,t0,t2;
119	PTR_ADD		t0,t1,t0;
120
1211:	LONG_L		s0, (t0)
122	bne		s0, zero,1b
123
124#ifdef CONFIG_CPU_CAVIUM_OCTEON
125	.set push
126	.set noreorder
127	synci		0($0)
128	.set pop
129#else
130	sync
131#endif
132	j		s1
133	END(kexec_smp_wait)
134#endif
135
136#ifdef __mips64
137       /* all PTR's must be aligned to 8 byte in 64-bit mode */
138       .align  3
139#endif
140
141/* All parameters to new kernel are passed in registers a0-a3.
142 * kexec_args[0..3] are uses to prepare register values.
143 */
144
145kexec_args:
146	EXPORT(kexec_args)
147arg0:	PTR		0x0
148arg1:	PTR		0x0
149arg2:	PTR		0x0
150arg3:	PTR		0x0
151	.size	kexec_args,PTRSIZE*4
152
153#ifdef CONFIG_SMP
154/*
155 * Secondary CPUs may have different kernel parameters in
156 * their registers a0-a3. secondary_kexec_args[0..3] are used
157 * to prepare register values.
158 */
159secondary_kexec_args:
160	EXPORT(secondary_kexec_args)
161s_arg0:	PTR		0x0
162s_arg1:	PTR		0x0
163s_arg2:	PTR		0x0
164s_arg3:	PTR		0x0
165	.size	secondary_kexec_args,PTRSIZE*4
166kexec_flag:
167	LONG		0x1
168
169#endif
170
171kexec_start_address:
172	EXPORT(kexec_start_address)
173	PTR		0x0
174	.size		kexec_start_address, PTRSIZE
175
176kexec_indirection_page:
177	EXPORT(kexec_indirection_page)
178	PTR		0
179	.size		kexec_indirection_page, PTRSIZE
180
181relocate_new_kernel_end:
182
183relocate_new_kernel_size:
184	EXPORT(relocate_new_kernel_size)
185	PTR		relocate_new_kernel_end - relocate_new_kernel
186	.size		relocate_new_kernel_size, PTRSIZE
187