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