1/*
2 * Copyright 2011 Tilera Corporation. All Rights Reserved.
3 *
4 *   This program is free software; you can redistribute it and/or
5 *   modify it under the terms of the GNU General Public License
6 *   as published by the Free Software Foundation, version 2.
7 *
8 *   This program is distributed in the hope that it will be useful, but
9 *   WITHOUT ANY WARRANTY; without even the implied warranty of
10 *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
11 *   NON INFRINGEMENT.  See the GNU General Public License for
12 *   more details.
13 *
14 * copy new kernel into place and then call hv_reexec
15 *
16 */
17
18#include <linux/linkage.h>
19#include <arch/chip.h>
20#include <asm/page.h>
21#include <hv/hypervisor.h>
22
23#undef RELOCATE_NEW_KERNEL_VERBOSE
24
25STD_ENTRY(relocate_new_kernel)
26
27	move	r30, r0		/* page list */
28	move	r31, r1		/* address of page we are on */
29	move	r32, r2		/* start address of new kernel */
30
31	shrui	r1, r1, PAGE_SHIFT
32	addi	r1, r1, 1
33	shli	sp, r1, PAGE_SHIFT
34	addi	sp, sp, -8
35	/* we now have a stack (whether we need one or not) */
36
37#ifdef RELOCATE_NEW_KERNEL_VERBOSE
38	moveli	r40, hw2_last(hv_console_putc)
39	shl16insli r40, r40, hw1(hv_console_putc)
40	shl16insli r40, r40, hw0(hv_console_putc)
41
42	moveli	r0, 'r'
43	jalr	r40
44
45	moveli	r0, '_'
46	jalr	r40
47
48	moveli	r0, 'n'
49	jalr	r40
50
51	moveli	r0, '_'
52	jalr	r40
53
54	moveli	r0, 'k'
55	jalr	r40
56
57	moveli	r0, '\n'
58	jalr	r40
59#endif
60
61	/*
62	 * Throughout this code r30 is pointer to the element of page
63	 * list we are working on.
64	 *
65	 * Normally we get to the next element of the page list by
66	 * incrementing r30 by eight.  The exception is if the element
67	 * on the page list is an IND_INDIRECTION in which case we use
68	 * the element with the low bits masked off as the new value
69	 * of r30.
70	 *
71	 * To get this started, we need the value passed to us (which
72	 * will always be an IND_INDIRECTION) in memory somewhere with
73	 * r30 pointing at it.  To do that, we push the value passed
74	 * to us on the stack and make r30 point to it.
75	 */
76
77	st	sp, r30
78	move	r30, sp
79	addi	sp, sp, -16
80
81	/*
82	 * On TILE-GX, we need to flush all tiles' caches, since we may
83	 * have been doing hash-for-home caching there.  Note that we
84	 * must do this _after_ we're completely done modifying any memory
85	 * other than our output buffer (which we know is locally cached).
86	 * We want the caches to be fully clean when we do the reexec,
87	 * because the hypervisor is going to do this flush again at that
88	 * point, and we don't want that second flush to overwrite any memory.
89	 */
90	{
91	 move	r0, zero	 /* cache_pa */
92	 moveli	r1, hw2_last(HV_FLUSH_EVICT_L2)
93	}
94	{
95	 shl16insli	r1, r1, hw1(HV_FLUSH_EVICT_L2)
96	 movei	r2, -1		 /* cache_cpumask; -1 means all client tiles */
97	}
98	{
99	 shl16insli	r1, r1, hw0(HV_FLUSH_EVICT_L2)  /* cache_control */
100	 move	r3, zero	 /* tlb_va */
101	}
102	{
103	 move	r4, zero	 /* tlb_length */
104	 move	r5, zero	 /* tlb_pgsize */
105	}
106	{
107	 move	r6, zero	 /* tlb_cpumask */
108	 move	r7, zero	 /* asids */
109	}
110	{
111	 moveli	r20, hw2_last(hv_flush_remote)
112	 move	r8, zero	 /* asidcount */
113	}
114	shl16insli	r20, r20, hw1(hv_flush_remote)
115	shl16insli	r20, r20, hw0(hv_flush_remote)
116
117	jalr	r20
118
119	/* r33 is destination pointer, default to zero */
120
121	moveli	r33, 0
122
123.Lloop:	ld	r10, r30
124
125	andi	r9, r10, 0xf	/* low 4 bits tell us what type it is */
126	xor	r10, r10, r9	/* r10 is now value with low 4 bits stripped */
127
128	cmpeqi	r0, r9, 0x1	/* IND_DESTINATION */
129	beqzt	r0, .Ltry2
130
131	move	r33, r10
132
133#ifdef RELOCATE_NEW_KERNEL_VERBOSE
134	moveli	r0, 'd'
135	jalr	r40
136#endif
137
138	addi	r30, r30, 8
139	j	.Lloop
140
141.Ltry2:
142	cmpeqi	r0, r9, 0x2	/* IND_INDIRECTION */
143	beqzt	r0, .Ltry4
144
145	move	r30, r10
146
147#ifdef RELOCATE_NEW_KERNEL_VERBOSE
148	moveli	r0, 'i'
149	jalr	r40
150#endif
151
152	j	.Lloop
153
154.Ltry4:
155	cmpeqi	r0, r9, 0x4	/* IND_DONE */
156	beqzt	r0, .Ltry8
157
158	mf
159
160#ifdef RELOCATE_NEW_KERNEL_VERBOSE
161	moveli	r0, 'D'
162	jalr	r40
163	moveli	r0, '\n'
164	jalr	r40
165#endif
166
167	move	r0, r32
168
169	moveli	r41, hw2_last(hv_reexec)
170	shl16insli	r41, r41, hw1(hv_reexec)
171	shl16insli	r41, r41, hw0(hv_reexec)
172
173	jalr	r41
174
175	/* we should not get here */
176
177#ifdef RELOCATE_NEW_KERNEL_VERBOSE
178	moveli	r0, '?'
179	jalr	r40
180	moveli	r0, '\n'
181	jalr	r40
182#endif
183
184	j	.Lhalt
185
186.Ltry8:	cmpeqi	r0, r9, 0x8	/* IND_SOURCE */
187	beqz	r0, .Lerr	/* unknown type */
188
189	/* copy page at r10 to page at r33 */
190
191	move	r11, r33
192
193	moveli	r0, hw2_last(PAGE_SIZE)
194	shl16insli	r0, r0, hw1(PAGE_SIZE)
195	shl16insli	r0, r0, hw0(PAGE_SIZE)
196	add	r33, r33, r0
197
198	/* copy word at r10 to word at r11 until r11 equals r33 */
199
200	/* We know page size must be multiple of 8, so we can unroll
201	 * 8 times safely without any edge case checking.
202	 *
203	 * Issue a flush of the destination every 8 words to avoid
204	 * incoherence when starting the new kernel.  (Now this is
205	 * just good paranoia because the hv_reexec call will also
206	 * take care of this.)
207	 */
208
2091:
210	{ ld	r0, r10; addi	r10, r10, 8 }
211	{ st	r11, r0; addi	r11, r11, 8 }
212	{ ld	r0, r10; addi	r10, r10, 8 }
213	{ st	r11, r0; addi	r11, r11, 8 }
214	{ ld	r0, r10; addi	r10, r10, 8 }
215	{ st	r11, r0; addi	r11, r11, 8 }
216	{ ld	r0, r10; addi	r10, r10, 8 }
217	{ st	r11, r0; addi	r11, r11, 8 }
218	{ ld	r0, r10; addi	r10, r10, 8 }
219	{ st	r11, r0; addi	r11, r11, 8 }
220	{ ld	r0, r10; addi	r10, r10, 8 }
221	{ st	r11, r0; addi	r11, r11, 8 }
222	{ ld	r0, r10; addi	r10, r10, 8 }
223	{ st	r11, r0; addi	r11, r11, 8 }
224	{ ld	r0, r10; addi	r10, r10, 8 }
225	{ st	r11, r0 }
226	{ flush r11    ; addi	r11, r11, 8 }
227
228	cmpeq	r0, r33, r11
229	beqzt	r0, 1b
230
231#ifdef RELOCATE_NEW_KERNEL_VERBOSE
232	moveli	r0, 's'
233	jalr	r40
234#endif
235
236	addi	r30, r30, 8
237	j	.Lloop
238
239
240.Lerr:
241#ifdef RELOCATE_NEW_KERNEL_VERBOSE
242	moveli	r0, 'e'
243	jalr	r40
244	moveli	r0, 'r'
245	jalr	r40
246	moveli	r0, 'r'
247	jalr	r40
248	moveli	r0, '\n'
249	jalr	r40
250#endif
251.Lhalt:
252	moveli r41, hw2_last(hv_halt)
253	shl16insli r41, r41, hw1(hv_halt)
254	shl16insli r41, r41, hw0(hv_halt)
255
256	jalr	r41
257	STD_ENDPROC(relocate_new_kernel)
258
259	.section .rodata,"a"
260
261	.globl relocate_new_kernel_size
262relocate_new_kernel_size:
263	.long .Lend_relocate_new_kernel - relocate_new_kernel
264