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: (const) kernel_map.va_pa_offset, used when switching MMU off
21	 * s5: Pointer to the destination address for the relocation
22	 * s6: (const) Physical address of the main loop
23	 */
24	mv	s0, a0
25	mv	s1, a1
26	mv	s2, a2
27	mv	s3, a3
28	mv	s4, a4
29	mv	s5, zero
30	mv	s6, zero
31
32	/* Disable / cleanup interrupts */
33	csrw	CSR_SIE, zero
34	csrw	CSR_SIP, zero
35
36	/*
37	 * When we switch SATP.MODE to "Bare" we'll only
38	 * play with physical addresses. However the first time
39	 * we try to jump somewhere, the offset on the jump
40	 * will be relative to pc which will still be on VA. To
41	 * deal with this we set stvec to the physical address at
42	 * the start of the loop below so that we jump there in
43	 * any case.
44	 */
45	la	s6, 1f
46	sub	s6, s6, s4
47	csrw	CSR_STVEC, s6
48
49	/*
50	 * With C-extension, here we get 42 Bytes and the next
51	 * .align directive would pad zeros here up to 44 Bytes.
52	 * So manually put a nop here to avoid zeros padding.
53	*/
54	nop
55
56	/* Process entries in a loop */
57.align 2
581:
59	REG_L	t0, 0(s0)		/* t0 = *image->entry */
60	addi	s0, s0, RISCV_SZPTR	/* image->entry++ */
61
62	/* IND_DESTINATION entry ? -> save destination address */
63	andi	t1, t0, 0x1
64	beqz	t1, 2f
65	andi	s5, t0, ~0x1
66	j	1b
67
682:
69	/* IND_INDIRECTION entry ? -> update next entry ptr (PA) */
70	andi	t1, t0, 0x2
71	beqz	t1, 2f
72	andi	s0, t0, ~0x2
73	csrw	CSR_SATP, zero
74	jr	s6
75
762:
77	/* IND_DONE entry ? -> jump to done label */
78	andi	t1, t0, 0x4
79	beqz	t1, 2f
80	j	4f
81
822:
83	/*
84	 * IND_SOURCE entry ? -> copy page word by word to the
85	 * destination address we got from IND_DESTINATION
86	 */
87	andi	t1, t0, 0x8
88	beqz	t1, 1b		/* Unknown entry type, ignore it */
89	andi	t0, t0, ~0x8
90	li	t3, (PAGE_SIZE / RISCV_SZPTR)	/* i = num words per page */
913:	/* copy loop */
92	REG_L	t1, (t0)	/* t1 = *src_ptr */
93	REG_S	t1, (s5)	/* *dst_ptr = *src_ptr */
94	addi	t0, t0, RISCV_SZPTR /* stc_ptr++ */
95	addi	s5, s5, RISCV_SZPTR /* dst_ptr++ */
96	addi	t3, t3, -0x1	/* i-- */
97	beqz	t3, 1b		/* copy done ? */
98	j	3b
99
1004:
101	/* Pass the arguments to the next kernel  / Cleanup*/
102	mv	a0, s3
103	mv	a1, s2
104	mv	a2, s1
105
106	/* Cleanup */
107	mv	a3, zero
108	mv	a4, zero
109	mv	a5, zero
110	mv	a6, zero
111	mv	a7, zero
112
113	mv	s0, zero
114	mv	s1, zero
115	mv	s2, zero
116	mv	s3, zero
117	mv	s4, zero
118	mv	s5, zero
119	mv	s6, zero
120	mv	s7, zero
121	mv	s8, zero
122	mv	s9, zero
123	mv	s10, zero
124	mv	s11, zero
125
126	mv	t0, zero
127	mv	t1, zero
128	mv	t2, zero
129	mv	t3, zero
130	mv	t4, zero
131	mv	t5, zero
132	mv	t6, zero
133	csrw	CSR_SEPC, zero
134	csrw	CSR_SCAUSE, zero
135	csrw	CSR_SSCRATCH, zero
136
137	/*
138	 * Make sure the relocated code is visible
139	 * and jump to the new kernel
140	 */
141	fence.i
142
143	jr	a2
144
145SYM_CODE_END(riscv_kexec_relocate)
146riscv_kexec_relocate_end:
147
148
149/* Used for jumping to crashkernel */
150.section ".text"
151SYM_CODE_START(riscv_kexec_norelocate)
152	/*
153	 * s0: (const) Phys address to jump to
154	 * s1: (const) Phys address of the FDT image
155	 * s2: (const) The hartid of the current hart
156	 */
157	mv	s0, a1
158	mv	s1, a2
159	mv	s2, a3
160
161	/* Disable / cleanup interrupts */
162	csrw	CSR_SIE, zero
163	csrw	CSR_SIP, zero
164
165	/* Pass the arguments to the next kernel  / Cleanup*/
166	mv	a0, s2
167	mv	a1, s1
168	mv	a2, s0
169
170	/* Cleanup */
171	mv	a3, zero
172	mv	a4, zero
173	mv	a5, zero
174	mv	a6, zero
175	mv	a7, zero
176
177	mv	s0, zero
178	mv	s1, zero
179	mv	s2, zero
180	mv	s3, zero
181	mv	s4, zero
182	mv	s5, zero
183	mv	s6, zero
184	mv	s7, zero
185	mv	s8, zero
186	mv	s9, zero
187	mv	s10, zero
188	mv	s11, zero
189
190	mv	t0, zero
191	mv	t1, zero
192	mv	t2, zero
193	mv	t3, zero
194	mv	t4, zero
195	mv	t5, zero
196	mv	t6, zero
197	csrw	CSR_SEPC, zero
198	csrw	CSR_SCAUSE, zero
199	csrw	CSR_SSCRATCH, zero
200
201	/*
202	 * Switch to physical addressing
203	 * This will also trigger a jump to CSR_STVEC
204	 * which in this case is the address of the new
205	 * kernel.
206	 */
207	csrw	CSR_STVEC, a2
208	csrw	CSR_SATP, zero
209
210SYM_CODE_END(riscv_kexec_norelocate)
211
212.section ".rodata"
213SYM_DATA(riscv_kexec_relocate_size,
214	.long riscv_kexec_relocate_end - riscv_kexec_relocate)
215
216