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