1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * Copyright (C) 2019 FORTH-ICS/CARV
4 *  Nick Kossifidis <mick@ics.forth.gr>
5 */
6
7#include <asm/asm.h>	/* For RISCV_* and REG_* macros */
8#include <asm/csr.h>	/* For CSR_* macros */
9#include <asm/page.h>	/* For PAGE_SIZE */
10#include <linux/linkage.h> /* For SYM_* macros */
11
12.section ".rodata"
13SYM_CODE_START(riscv_kexec_relocate)
14
15	/*
16	 * s0: Pointer to the current entry
17	 * s1: (const) Phys address to jump to after relocation
18	 * s2: (const) Phys address of the FDT image
19	 * s3: (const) The hartid of the current hart
20	 * s4: Pointer to the destination address for the relocation
21	 * s5: (const) Number of words per page
22	 * s6: (const) 1, used for subtraction
23	 * s7: (const) kernel_map.va_pa_offset, used when switching MMU off
24	 * s8: (const) Physical address of the main loop
25	 * s9: (debug) indirection page counter
26	 * s10: (debug) entry counter
27	 * s11: (debug) copied words counter
28	 */
29	mv	s0, a0
30	mv	s1, a1
31	mv	s2, a2
32	mv	s3, a3
33	mv	s4, zero
34	li	s5, (PAGE_SIZE / RISCV_SZPTR)
35	li	s6, 1
36	mv	s7, a4
37	mv	s8, zero
38	mv	s9, zero
39	mv	s10, zero
40	mv	s11, zero
41
42	/* Disable / cleanup interrupts */
43	csrw	CSR_SIE, zero
44	csrw	CSR_SIP, zero
45
46	/*
47	 * When we switch SATP.MODE to "Bare" we'll only
48	 * play with physical addresses. However the first time
49	 * we try to jump somewhere, the offset on the jump
50	 * will be relative to pc which will still be on VA. To
51	 * deal with this we set stvec to the physical address at
52	 * the start of the loop below so that we jump there in
53	 * any case.
54	 */
55	la	s8, 1f
56	sub	s8, s8, s7
57	csrw	CSR_STVEC, s8
58
59	/* Process entries in a loop */
60.align 2
611:
62	addi	s10, s10, 1
63	REG_L	t0, 0(s0)		/* t0 = *image->entry */
64	addi	s0, s0, RISCV_SZPTR	/* image->entry++ */
65
66	/* IND_DESTINATION entry ? -> save destination address */
67	andi	t1, t0, 0x1
68	beqz	t1, 2f
69	andi	s4, t0, ~0x1
70	j	1b
71
722:
73	/* IND_INDIRECTION entry ? -> update next entry ptr (PA) */
74	andi	t1, t0, 0x2
75	beqz	t1, 2f
76	andi	s0, t0, ~0x2
77	addi	s9, s9, 1
78	csrw	CSR_SATP, zero
79	jalr	zero, s8, 0
80
812:
82	/* IND_DONE entry ? -> jump to done label */
83	andi	t1, t0, 0x4
84	beqz	t1, 2f
85	j	4f
86
872:
88	/*
89	 * IND_SOURCE entry ? -> copy page word by word to the
90	 * destination address we got from IND_DESTINATION
91	 */
92	andi	t1, t0, 0x8
93	beqz	t1, 1b		/* Unknown entry type, ignore it */
94	andi	t0, t0, ~0x8
95	mv	t3, s5		/* i = num words per page */
963:	/* copy loop */
97	REG_L	t1, (t0)	/* t1 = *src_ptr */
98	REG_S	t1, (s4)	/* *dst_ptr = *src_ptr */
99	addi	t0, t0, RISCV_SZPTR /* stc_ptr++ */
100	addi	s4, s4, RISCV_SZPTR /* dst_ptr++ */
101	sub	t3, t3, s6	/* i-- */
102	addi	s11, s11, 1	/* c++ */
103	beqz	t3, 1b		/* copy done ? */
104	j	3b
105
1064:
107	/* Pass the arguments to the next kernel  / Cleanup*/
108	mv	a0, s3
109	mv	a1, s2
110	mv	a2, s1
111
112	/* Cleanup */
113	mv	a3, zero
114	mv	a4, zero
115	mv	a5, zero
116	mv	a6, zero
117	mv	a7, zero
118
119	mv	s0, zero
120	mv	s1, zero
121	mv	s2, zero
122	mv	s3, zero
123	mv	s4, zero
124	mv	s5, zero
125	mv	s6, zero
126	mv	s7, zero
127	mv	s8, zero
128	mv	s9, zero
129	mv	s10, zero
130	mv	s11, zero
131
132	mv	t0, zero
133	mv	t1, zero
134	mv	t2, zero
135	mv	t3, zero
136	mv	t4, zero
137	mv	t5, zero
138	mv	t6, zero
139	csrw	CSR_SEPC, zero
140	csrw	CSR_SCAUSE, zero
141	csrw	CSR_SSCRATCH, zero
142
143	/*
144	 * Make sure the relocated code is visible
145	 * and jump to the new kernel
146	 */
147	fence.i
148
149	jalr	zero, a2, 0
150
151SYM_CODE_END(riscv_kexec_relocate)
152riscv_kexec_relocate_end:
153
154
155/* Used for jumping to crashkernel */
156.section ".text"
157SYM_CODE_START(riscv_kexec_norelocate)
158	/*
159	 * s0: (const) Phys address to jump to
160	 * s1: (const) Phys address of the FDT image
161	 * s2: (const) The hartid of the current hart
162	 * s3: (const) kernel_map.va_pa_offset, used when switching MMU off
163	 */
164	mv	s0, a1
165	mv	s1, a2
166	mv	s2, a3
167	mv	s3, a4
168
169	/* Disable / cleanup interrupts */
170	csrw	CSR_SIE, zero
171	csrw	CSR_SIP, zero
172
173	/* Switch to physical addressing */
174	la	s4, 1f
175	sub	s4, s4, s3
176	csrw	CSR_STVEC, s4
177	csrw	CSR_SATP, zero
178
179.align 2
1801:
181	/* Pass the arguments to the next kernel  / Cleanup*/
182	mv	a0, s2
183	mv	a1, s1
184	mv	a2, s0
185
186	/* Cleanup */
187	mv	a3, zero
188	mv	a4, zero
189	mv	a5, zero
190	mv	a6, zero
191	mv	a7, zero
192
193	mv	s0, zero
194	mv	s1, zero
195	mv	s2, zero
196	mv	s3, zero
197	mv	s4, zero
198	mv	s5, zero
199	mv	s6, zero
200	mv	s7, zero
201	mv	s8, zero
202	mv	s9, zero
203	mv	s10, zero
204	mv	s11, zero
205
206	mv	t0, zero
207	mv	t1, zero
208	mv	t2, zero
209	mv	t3, zero
210	mv	t4, zero
211	mv	t5, zero
212	mv	t6, zero
213	csrw	CSR_SEPC, zero
214	csrw	CSR_SCAUSE, zero
215	csrw	CSR_SSCRATCH, zero
216
217	jalr	zero, a2, 0
218SYM_CODE_END(riscv_kexec_norelocate)
219
220.section ".rodata"
221SYM_DATA(riscv_kexec_relocate_size,
222	.long riscv_kexec_relocate_end - riscv_kexec_relocate)
223
224