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 <elf.h>
15#include <system-constants.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#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
94	li	t0, CONFIG_SPL_STACK
95#else
96	li	t0, SYS_INIT_SP_ADDR
97#endif
98	and	t0, t0, -16		/* force 16 byte alignment */
99
100	/* setup stack */
101#if CONFIG_IS_ENABLED(SMP)
102	/* tp: hart id */
103	slli	t1, tp, CONFIG_STACK_SIZE_SHIFT
104	sub	sp, t0, t1
105#else
106	mv	sp, t0
107#endif
108/*
109 * Now sp points to the right stack belonging to current CPU.
110 * It's essential before any function call, otherwise, we get data-race.
111 */
112
113/* clear stack if necessary */
114#if CONFIG_IS_ENABLED(ZERO_MEM_BEFORE_USE)
115clear_stack:
116	li	t1, 1
117	slli	t1, t1, CONFIG_STACK_SIZE_SHIFT
118	sub	t1, sp, t1
119clear_stack_loop:
120	SREG	zero, 0(t1)		/* t1 is always 16 byte aligned */
121	addi	t1, t1, REGBYTES
122	blt	t1, sp, clear_stack_loop
123#endif
124
125call_board_init_f_0:
126	/* find top of reserve space */
127#if CONFIG_IS_ENABLED(SMP)
128	li	t1, CONFIG_NR_CPUS
129#else
130	li	t1, 1
131#endif
132	slli	t1, t1, CONFIG_STACK_SIZE_SHIFT
133	sub	a0, t0, t1		/* t1 -> size of all CPU stacks */
134	jal	board_init_f_alloc_reserve
135
136	/*
137	 * Save global data pointer for later. We don't set it here because it
138	 * is not initialized yet.
139	 */
140	mv	s0, a0
141
142
143	/* Configure proprietary settings and customized CSRs of harts */
144call_harts_early_init:
145	jal	harts_early_init
146
147#if !CONFIG_IS_ENABLED(XIP)
148	/*
149	 * Pick hart to initialize global data and run U-Boot. The other harts
150	 * wait for initialization to complete.
151	 */
152	la	t0, hart_lottery
153	li	t1, 1
154	amoswap.w s2, t1, 0(t0)
155	bnez	s2, wait_for_gd_init
156#else
157	/*
158	 * FIXME: gp is set before it is initialized. If an XIP U-Boot ever
159	 * encounters a pending IPI on boot it is liable to jump to whatever
160	 * memory happens to be in ipi_data.addr on boot. It may also run into
161	 * problems if it encounters an exception too early (because printf/puts
162	 * accesses gd).
163	 */
164	mv	gp, s0
165#if CONFIG_IS_ENABLED(RISCV_MMODE)
166	bnez	tp, secondary_hart_loop
167#endif
168#endif
169
170	mv      a0, s0
171	jal	board_init_f_init_reserve
172
173	SREG	s1, GD_FIRMWARE_FDT_ADDR(gp)
174	/* save the boot hart id to global_data */
175	SREG	tp, GD_BOOT_HART(gp)
176
177#if !CONFIG_IS_ENABLED(XIP)
178#ifdef CONFIG_AVAILABLE_HARTS
179	la	t0, available_harts_lock
180	amoswap.w.rl zero, zero, 0(t0)
181#endif
182
183wait_for_gd_init:
184	/*
185	 * Set the global data pointer only when gd_t has been initialized.
186	 * This was already set by arch_setup_gd on the boot hart, but all other
187	 * harts' global data pointers gets set here.
188	 */
189	mv	gp, s0
190#ifdef CONFIG_AVAILABLE_HARTS
191	la	t0, available_harts_lock
192	li	t1, 1
1931:	amoswap.w.aq t1, t1, 0(t0)
194	bnez	t1, 1b
195
196	/* register available harts in the available_harts mask */
197	li	t1, 1
198	sll	t1, t1, tp
199	LREG	t2, GD_AVAILABLE_HARTS(gp)
200	or	t2, t2, t1
201	SREG	t2, GD_AVAILABLE_HARTS(gp)
202
203	amoswap.w.rl zero, zero, 0(t0)
204#endif
205
206	/*
207	 * Continue on hart lottery winner, others branch to
208	 * secondary_hart_loop.
209	 */
210	bnez	s2, secondary_hart_loop
211#endif
212
213	/* Enable cache */
214	jal	icache_enable
215	jal	dcache_enable
216
217#ifdef CONFIG_DEBUG_UART
218	jal	debug_uart_init
219#endif
220
221	mv	a0, zero		/* a0 <-- boot_flags = 0 */
222	la	t5, board_init_f
223	jalr	t5			/* jump to board_init_f() */
224
225#ifdef CONFIG_SPL_BUILD
226spl_clear_bss:
227	la	t0, __bss_start
228	la	t1, __bss_end
229	beq	t0, t1, spl_stack_gd_setup
230
231spl_clear_bss_loop:
232	SREG	zero, 0(t0)
233	addi	t0, t0, REGBYTES
234	blt	t0, t1, spl_clear_bss_loop
235
236spl_stack_gd_setup:
237	jal	spl_relocate_stack_gd
238
239	/* skip setup if we did not relocate */
240	beqz	a0, spl_call_board_init_r
241	mv	s0, a0
242
243	/* setup stack on main hart */
244#if CONFIG_IS_ENABLED(SMP)
245	/* tp: hart id */
246	slli	t0, tp, CONFIG_STACK_SIZE_SHIFT
247	sub	sp, s0, t0
248#else
249	mv	sp, s0
250#endif
251
252#if CONFIG_IS_ENABLED(SMP)
253	/* set new stack and global data pointer on secondary harts */
254spl_secondary_hart_stack_gd_setup:
255	la	a0, secondary_hart_relocate
256	mv	a1, s0
257	mv	a2, s0
258	mv	a3, zero
259	jal	smp_call_function
260
261	/* hang if relocation of secondary harts has failed */
262	beqz	a0, 1f
263	mv	a1, a0
264	la	a0, secondary_harts_relocation_error
265	jal	printf
266	jal	hang
267#endif
268
269	/* set new global data pointer on main hart */
2701:	mv	gp, s0
271
272spl_call_board_init_r:
273	mv	a0, zero
274	mv	a1, zero
275	j	board_init_r
276#endif
277
278#if !defined(CONFIG_SPL_BUILD)
279/*
280 * void relocate_code(addr_sp, gd, addr_moni)
281 *
282 * This "function" does not return, instead it continues in RAM
283 * after relocating the monitor code.
284 *
285 */
286.globl relocate_code
287relocate_code:
288	mv	s2, a0			/* save addr_sp */
289	mv	s3, a1			/* save addr of gd */
290	mv	s4, a2			/* save addr of destination */
291
292/*
293 *Set up the stack
294 */
295stack_setup:
296#if CONFIG_IS_ENABLED(SMP)
297	/* tp: hart id */
298	slli	t0, tp, CONFIG_STACK_SIZE_SHIFT
299	sub	sp, s2, t0
300#else
301	mv	sp, s2
302#endif
303
304	la	t0, _start
305	sub	t6, s4, t0		/* t6 <- relocation offset */
306	beq	t0, s4, clear_bss	/* skip relocation */
307
308	mv	t1, s4			/* t1 <- scratch for copy_loop */
309	la	t2, __bss_start		/* t2 <- source end address */
310
311copy_loop:
312	LREG	t5, 0(t0)
313	addi	t0, t0, REGBYTES
314	SREG	t5, 0(t1)
315	addi	t1, t1, REGBYTES
316	blt	t0, t2, copy_loop
317
318/*
319 * Update dynamic relocations after board_init_f
320 */
321fix_rela_dyn:
322	la	t1, __rel_dyn_start
323	la	t2, __rel_dyn_end
324	beq	t1, t2, clear_bss
325	add	t1, t1, t6		/* t1 <- rela_dyn_start in RAM */
326	add	t2, t2, t6		/* t2 <- rela_dyn_end in RAM */
327
3286:
329	LREG	t5, REGBYTES(t1)	/* t5 <-- relocation info:type */
330	li	t3, R_RISCV_RELATIVE	/* reloc type R_RISCV_RELATIVE */
331	bne	t5, t3, 8f		/* skip non-RISCV_RELOC entries */
332	LREG	t3, 0(t1)
333	LREG	t5, (REGBYTES * 2)(t1)	/* t5 <-- addend */
334	add	t5, t5, t6		/* t5 <-- location to fix up in RAM */
335	add	t3, t3, t6		/* t3 <-- location to fix up in RAM */
336	SREG	t5, 0(t3)
337	j	10f
338
3398:
340	la	t4, __dyn_sym_start
341	add	t4, t4, t6
342
3439:
344	srli	t0, t5, SYM_INDEX	/* t0 <--- sym table index */
345	andi	t5, t5, 0xFF		/* t5 <--- relocation type */
346	li	t3, RELOC_TYPE
347	bne	t5, t3, 10f		/* skip non-addned entries */
348
349	LREG	t3, 0(t1)
350	li	t5, SYM_SIZE
351	mul	t0, t0, t5
352	add	s5, t4, t0
353	LREG	t0, (REGBYTES * 2)(t1)	/* t0 <-- addend */
354	LREG	t5, REGBYTES(s5)
355	add	t5, t5, t0
356	add	t5, t5, t6		/* t5 <-- location to fix up in RAM */
357	add	t3, t3, t6		/* t3 <-- location to fix up in RAM */
358	SREG	t5, 0(t3)
35910:
360	addi	t1, t1, (REGBYTES * 3)
361	blt	t1, t2, 6b
362
363/*
364 * trap update
365*/
366	la	t0, trap_entry
367	add	t0, t0, t6
368	csrw	MODE_PREFIX(tvec), t0
369
370clear_bss:
371	la	t0, __bss_start		/* t0 <- rel __bss_start in FLASH */
372	add	t0, t0, t6		/* t0 <- rel __bss_start in RAM */
373	la	t1, __bss_end		/* t1 <- rel __bss_end in FLASH */
374	add	t1, t1, t6		/* t1 <- rel __bss_end in RAM */
375	beq	t0, t1, relocate_secondary_harts
376
377clbss_l:
378	SREG	zero, 0(t0)		/* clear loop... */
379	addi	t0, t0, REGBYTES
380	blt	t0, t1, clbss_l
381
382relocate_secondary_harts:
383#if CONFIG_IS_ENABLED(SMP)
384	/* send relocation IPI */
385	la	t0, secondary_hart_relocate
386	add	a0, t0, t6
387
388	/* store relocation offset */
389	mv	s5, t6
390
391	mv	a1, s2
392	mv	a2, s3
393	mv	a3, zero
394	jal	smp_call_function
395
396	/* hang if relocation of secondary harts has failed */
397	beqz	a0, 1f
398	mv	a1, a0
399	la	a0, secondary_harts_relocation_error
400	jal	printf
401	jal	hang
402
403	/* restore relocation offset */
4041:	mv	t6, s5
405#endif
406
407/*
408 * We are done. Do not return, instead branch to second part of board
409 * initialization, now running from RAM.
410 */
411call_board_init_r:
412	jal	invalidate_icache_all
413	jal	flush_dcache_all
414	la	t0, board_init_r        /* offset of board_init_r() */
415	add	t4, t0, t6		/* real address of board_init_r() */
416/*
417 * setup parameters for board_init_r
418 */
419	mv	a0, s3			/* gd_t */
420	mv	a1, s4			/* dest_addr */
421	mv	s0, zero		/* fp == NULL */
422
423/*
424 * jump to it ...
425 */
426	jr	t4			/* jump to board_init_r() */
427#endif /* !defined(CONFIG_SPL_BUILD) */
428
429#if CONFIG_IS_ENABLED(SMP)
430hart_out_of_bounds_loop:
431	/* Harts in this loop are out of bounds, increase CONFIG_NR_CPUS. */
432	wfi
433	j	hart_out_of_bounds_loop
434
435/* SMP relocation entry */
436secondary_hart_relocate:
437	/* a1: new sp */
438	/* a2: new gd */
439	/* tp: hart id */
440
441	/* setup stack */
442	slli	t0, tp, CONFIG_STACK_SIZE_SHIFT
443	sub	sp, a1, t0
444
445	/* update global data pointer */
446	mv	gp, a2
447#endif
448
449/*
450 * Interrupts are disabled globally, but they can still be read from m/sip. The
451 * wfi function will wake us up if we get an IPI, even if we do not trap.
452 */
453secondary_hart_loop:
454	wfi
455
456#if CONFIG_IS_ENABLED(SMP)
457	csrr	t0, MODE_PREFIX(ip)
458#if CONFIG_IS_ENABLED(RISCV_MMODE)
459	andi	t0, t0, MIE_MSIE
460#else
461	andi	t0, t0, SIE_SSIE
462#endif
463	beqz	t0, secondary_hart_loop
464
465	mv	a0, tp
466	jal	handle_ipi
467#endif
468
469	j	secondary_hart_loop
470