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