1/* SPDX-License-Identifier: GPL-2.0+ */
2/*
3 * Startup Code for RISC-V Core
4 *
5 * Copyright (c) 2017 Microsemi Corporation.
6 * Copyright (c) 2017 Padmarao Begari <Padmarao.Begari@microsemi.com>
7 *
8 * Copyright (C) 2017 Andes Technology Corporation
9 * Rick Chen, Andes Technology Corporation <rick@andestech.com>
10 */
11
12#include <asm-offsets.h>
13#include <config.h>
14#include <common.h>
15#include <elf.h>
16#include <asm/encoding.h>
17#include <generated/asm-offsets.h>
18
19#ifdef CONFIG_32BIT
20#define LREG			lw
21#define SREG			sw
22#define REGBYTES		4
23#define RELOC_TYPE		R_RISCV_32
24#define SYM_INDEX		0x8
25#define SYM_SIZE		0x10
26#else
27#define LREG			ld
28#define SREG			sd
29#define REGBYTES		8
30#define RELOC_TYPE		R_RISCV_64
31#define SYM_INDEX		0x20
32#define SYM_SIZE		0x18
33#endif
34
35.section .data
36secondary_harts_relocation_error:
37	.ascii "Relocation of secondary harts has failed, error %d\n"
38
39.section .text
40.globl _start
41_start:
42#if CONFIG_IS_ENABLED(RISCV_MMODE)
43	csrr	a0, CSR_MHARTID
44#endif
45
46	/*
47	 * Save hart id and dtb pointer. The thread pointer register is not
48	 * modified by C code. It is used by secondary_hart_loop.
49	 */
50	mv	tp, a0
51	mv	s1, a1
52
53	/*
54	 * Set the global data pointer to a known value in case we get a very
55	 * early trap. The global data pointer will be set its actual value only
56	 * after it has been initialized.
57	 */
58	mv	gp, zero
59
60	/*
61	 * Set the trap handler. This must happen after initializing gp because
62	 * the handler may use it.
63	 */
64	la	t0, trap_entry
65	csrw	MODE_PREFIX(tvec), t0
66
67	/*
68	 * Mask all interrupts. Interrupts are disabled globally (in m/sstatus)
69	 * for U-Boot, but we will need to read m/sip to determine if we get an
70	 * IPI
71	 */
72	csrw	MODE_PREFIX(ie), zero
73
74#if CONFIG_IS_ENABLED(SMP)
75	/* check if hart is within range */
76	/* tp: hart id */
77	li	t0, CONFIG_NR_CPUS
78	bge	tp, t0, hart_out_of_bounds_loop
79
80	/* set xSIE bit to receive IPIs */
81#if CONFIG_IS_ENABLED(RISCV_MMODE)
82	li	t0, MIE_MSIE
83#else
84	li	t0, SIE_SSIE
85#endif
86	csrs	MODE_PREFIX(ie), t0
87#endif
88
89/*
90 * Set stackpointer in internal/ex RAM to call board_init_f
91 */
92call_board_init_f:
93	li	t0, -16
94#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
95	li	t1, CONFIG_SPL_STACK
96#else
97	li	t1, CONFIG_SYS_INIT_SP_ADDR
98#endif
99	and	sp, t1, t0		/* force 16 byte alignment */
100
101call_board_init_f_0:
102	mv	a0, sp
103	jal	board_init_f_alloc_reserve
104
105	/*
106	 * Save global data pointer for later. We don't set it here because it
107	 * is not initialized yet.
108	 */
109	mv	s0, a0
110
111	/* setup stack */
112#if CONFIG_IS_ENABLED(SMP)
113	/* tp: hart id */
114	slli	t0, tp, CONFIG_STACK_SIZE_SHIFT
115	sub	sp, a0, t0
116#else
117	mv	sp, a0
118#endif
119
120	/* Configure proprietary settings and customized CSRs of harts */
121call_harts_early_init:
122	jal	harts_early_init
123
124#ifndef CONFIG_XIP
125	/*
126	 * Pick hart to initialize global data and run U-Boot. The other harts
127	 * wait for initialization to complete.
128	 */
129	la	t0, hart_lottery
130	li	t1, 1
131	amoswap.w s2, t1, 0(t0)
132	bnez	s2, wait_for_gd_init
133#else
134	/*
135	 * FIXME: gp is set before it is initialized. If an XIP U-Boot ever
136	 * encounters a pending IPI on boot it is liable to jump to whatever
137	 * memory happens to be in ipi_data.addr on boot. It may also run into
138	 * problems if it encounters an exception too early (because printf/puts
139	 * accesses gd).
140	 */
141	mv	gp, s0
142	bnez	tp, secondary_hart_loop
143#endif
144
145#ifdef CONFIG_OF_PRIOR_STAGE
146	la	t0, prior_stage_fdt_address
147	SREG	s1, 0(t0)
148#endif
149
150	jal	board_init_f_init_reserve
151
152	SREG	s1, GD_FIRMWARE_FDT_ADDR(gp)
153	/* save the boot hart id to global_data */
154	SREG	tp, GD_BOOT_HART(gp)
155
156#ifndef CONFIG_XIP
157	la	t0, available_harts_lock
158	amoswap.w.rl zero, zero, 0(t0)
159
160wait_for_gd_init:
161	la	t0, available_harts_lock
162	li	t1, 1
1631:	amoswap.w.aq t1, t1, 0(t0)
164	bnez	t1, 1b
165
166	/*
167	 * Set the global data pointer only when gd_t has been initialized.
168	 * This was already set by arch_setup_gd on the boot hart, but all other
169	 * harts' global data pointers gets set here.
170	 */
171	mv	gp, s0
172
173	/* register available harts in the available_harts mask */
174	li	t1, 1
175	sll	t1, t1, tp
176	LREG	t2, GD_AVAILABLE_HARTS(gp)
177	or	t2, t2, t1
178	SREG	t2, GD_AVAILABLE_HARTS(gp)
179
180	amoswap.w.rl zero, zero, 0(t0)
181
182	/*
183	 * Continue on hart lottery winner, others branch to
184	 * secondary_hart_loop.
185	 */
186	bnez	s2, secondary_hart_loop
187#endif
188
189	/* Enable cache */
190	jal	icache_enable
191	jal	dcache_enable
192
193#ifdef CONFIG_DEBUG_UART
194	jal	debug_uart_init
195#endif
196
197	mv	a0, zero		/* a0 <-- boot_flags = 0 */
198	la	t5, board_init_f
199	jalr	t5			/* jump to board_init_f() */
200
201#ifdef CONFIG_SPL_BUILD
202spl_clear_bss:
203	la	t0, __bss_start
204	la	t1, __bss_end
205	beq	t0, t1, spl_stack_gd_setup
206
207spl_clear_bss_loop:
208	SREG	zero, 0(t0)
209	addi	t0, t0, REGBYTES
210	blt	t0, t1, spl_clear_bss_loop
211
212spl_stack_gd_setup:
213	jal	spl_relocate_stack_gd
214
215	/* skip setup if we did not relocate */
216	beqz	a0, spl_call_board_init_r
217	mv	s0, a0
218
219	/* setup stack on main hart */
220#if CONFIG_IS_ENABLED(SMP)
221	/* tp: hart id */
222	slli	t0, tp, CONFIG_STACK_SIZE_SHIFT
223	sub	sp, s0, t0
224#else
225	mv	sp, s0
226#endif
227
228#if CONFIG_IS_ENABLED(SMP)
229	/* set new stack and global data pointer on secondary harts */
230spl_secondary_hart_stack_gd_setup:
231	la	a0, secondary_hart_relocate
232	mv	a1, s0
233	mv	a2, s0
234	mv	a3, zero
235	jal	smp_call_function
236
237	/* hang if relocation of secondary harts has failed */
238	beqz	a0, 1f
239	mv	a1, a0
240	la	a0, secondary_harts_relocation_error
241	jal	printf
242	jal	hang
243#endif
244
245	/* set new global data pointer on main hart */
2461:	mv	gp, s0
247
248spl_call_board_init_r:
249	mv	a0, zero
250	mv	a1, zero
251	jal	board_init_r
252#endif
253
254/*
255 * void relocate_code(addr_sp, gd, addr_moni)
256 *
257 * This "function" does not return, instead it continues in RAM
258 * after relocating the monitor code.
259 *
260 */
261.globl relocate_code
262relocate_code:
263	mv	s2, a0			/* save addr_sp */
264	mv	s3, a1			/* save addr of gd */
265	mv	s4, a2			/* save addr of destination */
266
267/*
268 *Set up the stack
269 */
270stack_setup:
271#if CONFIG_IS_ENABLED(SMP)
272	/* tp: hart id */
273	slli	t0, tp, CONFIG_STACK_SIZE_SHIFT
274	sub	sp, s2, t0
275#else
276	mv	sp, s2
277#endif
278
279	la	t0, _start
280	sub	t6, s4, t0		/* t6 <- relocation offset */
281	beq	t0, s4, clear_bss	/* skip relocation */
282
283	mv	t1, s4			/* t1 <- scratch for copy_loop */
284	la	t3, __bss_start
285	sub	t3, t3, t0		/* t3 <- __bss_start_ofs */
286	add	t2, t0, t3		/* t2 <- source end address */
287
288copy_loop:
289	LREG	t5, 0(t0)
290	addi	t0, t0, REGBYTES
291	SREG	t5, 0(t1)
292	addi	t1, t1, REGBYTES
293	blt	t0, t2, copy_loop
294
295/*
296 * Update dynamic relocations after board_init_f
297 */
298fix_rela_dyn:
299	la	t1, __rel_dyn_start
300	la	t2, __rel_dyn_end
301	beq	t1, t2, clear_bss
302	add	t1, t1, t6		/* t1 <- rela_dyn_start in RAM */
303	add	t2, t2, t6		/* t2 <- rela_dyn_end in RAM */
304
305/*
306 * skip first reserved entry: address, type, addend
307 */
308	j	10f
309
3106:
311	LREG	t5, -(REGBYTES*2)(t1)	/* t5 <-- relocation info:type */
312	li	t3, R_RISCV_RELATIVE	/* reloc type R_RISCV_RELATIVE */
313	bne	t5, t3, 8f		/* skip non-RISCV_RELOC entries */
314	LREG	t3, -(REGBYTES*3)(t1)
315	LREG	t5, -(REGBYTES)(t1)	/* t5 <-- addend */
316	add	t5, t5, t6		/* t5 <-- location to fix up in RAM */
317	add	t3, t3, t6		/* t3 <-- location to fix up in RAM */
318	SREG	t5, 0(t3)
319	j	10f
320
3218:
322	la	t4, __dyn_sym_start
323	add	t4, t4, t6
324
3259:
326	LREG	t5, -(REGBYTES*2)(t1)	/* t5 <-- relocation info:type */
327	srli	t0, t5, SYM_INDEX	/* t0 <--- sym table index */
328	andi	t5, t5, 0xFF		/* t5 <--- relocation type */
329	li	t3, RELOC_TYPE
330	bne	t5, t3, 10f		/* skip non-addned entries */
331
332	LREG	t3, -(REGBYTES*3)(t1)
333	li	t5, SYM_SIZE
334	mul	t0, t0, t5
335	add	s5, t4, t0
336	LREG	t0, -(REGBYTES)(t1)	/* t0 <-- addend */
337	LREG	t5, REGBYTES(s5)
338	add	t5, t5, t0
339	add	t5, t5, t6		/* t5 <-- location to fix up in RAM */
340	add	t3, t3, t6		/* t3 <-- location to fix up in RAM */
341	SREG	t5, 0(t3)
34210:
343	addi	t1, t1, (REGBYTES*3)
344	ble	t1, t2, 6b
345
346/*
347 * trap update
348*/
349	la	t0, trap_entry
350	add	t0, t0, t6
351	csrw	MODE_PREFIX(tvec), t0
352
353clear_bss:
354	la	t0, __bss_start		/* t0 <- rel __bss_start in FLASH */
355	add	t0, t0, t6		/* t0 <- rel __bss_start in RAM */
356	la	t1, __bss_end		/* t1 <- rel __bss_end in FLASH */
357	add	t1, t1, t6		/* t1 <- rel __bss_end in RAM */
358	beq	t0, t1, relocate_secondary_harts
359
360clbss_l:
361	SREG	zero, 0(t0)		/* clear loop... */
362	addi	t0, t0, REGBYTES
363	blt	t0, t1, clbss_l
364
365relocate_secondary_harts:
366#if CONFIG_IS_ENABLED(SMP)
367	/* send relocation IPI */
368	la	t0, secondary_hart_relocate
369	add	a0, t0, t6
370
371	/* store relocation offset */
372	mv	s5, t6
373
374	mv	a1, s2
375	mv	a2, s3
376	mv	a3, zero
377	jal	smp_call_function
378
379	/* hang if relocation of secondary harts has failed */
380	beqz	a0, 1f
381	mv	a1, a0
382	la	a0, secondary_harts_relocation_error
383	jal	printf
384	jal	hang
385
386	/* restore relocation offset */
3871:	mv	t6, s5
388#endif
389
390/*
391 * We are done. Do not return, instead branch to second part of board
392 * initialization, now running from RAM.
393 */
394call_board_init_r:
395	jal	invalidate_icache_all
396	jal	flush_dcache_all
397	la	t0, board_init_r        /* offset of board_init_r() */
398	add	t4, t0, t6		/* real address of board_init_r() */
399/*
400 * setup parameters for board_init_r
401 */
402	mv	a0, s3			/* gd_t */
403	mv	a1, s4			/* dest_addr */
404
405/*
406 * jump to it ...
407 */
408	jr	t4			/* jump to board_init_r() */
409
410#if CONFIG_IS_ENABLED(SMP)
411hart_out_of_bounds_loop:
412	/* Harts in this loop are out of bounds, increase CONFIG_NR_CPUS. */
413	wfi
414	j	hart_out_of_bounds_loop
415
416/* SMP relocation entry */
417secondary_hart_relocate:
418	/* a1: new sp */
419	/* a2: new gd */
420	/* tp: hart id */
421
422	/* setup stack */
423	slli	t0, tp, CONFIG_STACK_SIZE_SHIFT
424	sub	sp, a1, t0
425
426	/* update global data pointer */
427	mv	gp, a2
428#endif
429
430/*
431 * Interrupts are disabled globally, but they can still be read from m/sip. The
432 * wfi function will wake us up if we get an IPI, even if we do not trap.
433 */
434secondary_hart_loop:
435	wfi
436
437#if CONFIG_IS_ENABLED(SMP)
438	csrr	t0, MODE_PREFIX(ip)
439#if CONFIG_IS_ENABLED(RISCV_MMODE)
440	andi	t0, t0, MIE_MSIE
441#else
442	andi	t0, t0, SIE_SSIE
443#endif
444	beqz	t0, secondary_hart_loop
445
446	mv	a0, tp
447	jal	handle_ipi
448#endif
449
450	j	secondary_hart_loop
451