mirror of
				https://xff.cz/git/u-boot/
				synced 2025-10-31 10:26:10 +01:00 
			
		
		
		
	Add a callback harts_early_init() to start.S to allow different riscv hart perform setup code for each hart as early as possible. Since all the harts enter the callback, they must be able to run the same setup. Signed-off-by: Green Wan <green.wan@sifive.com> Reviewed-by: Rick Chen <rick@andestech.com> Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
		
			
				
	
	
		
			451 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
			
		
		
	
	
			451 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
| /* SPDX-License-Identifier: GPL-2.0+ */
 | |
| /*
 | |
|  * Startup Code for RISC-V Core
 | |
|  *
 | |
|  * Copyright (c) 2017 Microsemi Corporation.
 | |
|  * Copyright (c) 2017 Padmarao Begari <Padmarao.Begari@microsemi.com>
 | |
|  *
 | |
|  * Copyright (C) 2017 Andes Technology Corporation
 | |
|  * Rick Chen, Andes Technology Corporation <rick@andestech.com>
 | |
|  */
 | |
| 
 | |
| #include <asm-offsets.h>
 | |
| #include <config.h>
 | |
| #include <common.h>
 | |
| #include <elf.h>
 | |
| #include <asm/encoding.h>
 | |
| #include <generated/asm-offsets.h>
 | |
| 
 | |
| #ifdef CONFIG_32BIT
 | |
| #define LREG			lw
 | |
| #define SREG			sw
 | |
| #define REGBYTES		4
 | |
| #define RELOC_TYPE		R_RISCV_32
 | |
| #define SYM_INDEX		0x8
 | |
| #define SYM_SIZE		0x10
 | |
| #else
 | |
| #define LREG			ld
 | |
| #define SREG			sd
 | |
| #define REGBYTES		8
 | |
| #define RELOC_TYPE		R_RISCV_64
 | |
| #define SYM_INDEX		0x20
 | |
| #define SYM_SIZE		0x18
 | |
| #endif
 | |
| 
 | |
| .section .data
 | |
| secondary_harts_relocation_error:
 | |
| 	.ascii "Relocation of secondary harts has failed, error %d\n"
 | |
| 
 | |
| .section .text
 | |
| .globl _start
 | |
| _start:
 | |
| #if CONFIG_IS_ENABLED(RISCV_MMODE)
 | |
| 	csrr	a0, CSR_MHARTID
 | |
| #endif
 | |
| 
 | |
| 	/*
 | |
| 	 * Save hart id and dtb pointer. The thread pointer register is not
 | |
| 	 * modified by C code. It is used by secondary_hart_loop.
 | |
| 	 */
 | |
| 	mv	tp, a0
 | |
| 	mv	s1, a1
 | |
| 
 | |
| 	/*
 | |
| 	 * Set the global data pointer to a known value in case we get a very
 | |
| 	 * early trap. The global data pointer will be set its actual value only
 | |
| 	 * after it has been initialized.
 | |
| 	 */
 | |
| 	mv	gp, zero
 | |
| 
 | |
| 	/*
 | |
| 	 * Set the trap handler. This must happen after initializing gp because
 | |
| 	 * the handler may use it.
 | |
| 	 */
 | |
| 	la	t0, trap_entry
 | |
| 	csrw	MODE_PREFIX(tvec), t0
 | |
| 
 | |
| 	/*
 | |
| 	 * Mask all interrupts. Interrupts are disabled globally (in m/sstatus)
 | |
| 	 * for U-Boot, but we will need to read m/sip to determine if we get an
 | |
| 	 * IPI
 | |
| 	 */
 | |
| 	csrw	MODE_PREFIX(ie), zero
 | |
| 
 | |
| #if CONFIG_IS_ENABLED(SMP)
 | |
| 	/* check if hart is within range */
 | |
| 	/* tp: hart id */
 | |
| 	li	t0, CONFIG_NR_CPUS
 | |
| 	bge	tp, t0, hart_out_of_bounds_loop
 | |
| 
 | |
| 	/* set xSIE bit to receive IPIs */
 | |
| #if CONFIG_IS_ENABLED(RISCV_MMODE)
 | |
| 	li	t0, MIE_MSIE
 | |
| #else
 | |
| 	li	t0, SIE_SSIE
 | |
| #endif
 | |
| 	csrs	MODE_PREFIX(ie), t0
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|  * Set stackpointer in internal/ex RAM to call board_init_f
 | |
|  */
 | |
| call_board_init_f:
 | |
| 	li	t0, -16
 | |
| #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
 | |
| 	li	t1, CONFIG_SPL_STACK
 | |
| #else
 | |
| 	li	t1, CONFIG_SYS_INIT_SP_ADDR
 | |
| #endif
 | |
| 	and	sp, t1, t0		/* force 16 byte alignment */
 | |
| 
 | |
| call_board_init_f_0:
 | |
| 	mv	a0, sp
 | |
| 	jal	board_init_f_alloc_reserve
 | |
| 
 | |
| 	/*
 | |
| 	 * Save global data pointer for later. We don't set it here because it
 | |
| 	 * is not initialized yet.
 | |
| 	 */
 | |
| 	mv	s0, a0
 | |
| 
 | |
| 	/* setup stack */
 | |
| #if CONFIG_IS_ENABLED(SMP)
 | |
| 	/* tp: hart id */
 | |
| 	slli	t0, tp, CONFIG_STACK_SIZE_SHIFT
 | |
| 	sub	sp, a0, t0
 | |
| #else
 | |
| 	mv	sp, a0
 | |
| #endif
 | |
| 
 | |
| 	/* Configure proprietary settings and customized CSRs of harts */
 | |
| call_harts_early_init:
 | |
| 	jal	harts_early_init
 | |
| 
 | |
| #ifndef CONFIG_XIP
 | |
| 	/*
 | |
| 	 * Pick hart to initialize global data and run U-Boot. The other harts
 | |
| 	 * wait for initialization to complete.
 | |
| 	 */
 | |
| 	la	t0, hart_lottery
 | |
| 	li	t1, 1
 | |
| 	amoswap.w s2, t1, 0(t0)
 | |
| 	bnez	s2, wait_for_gd_init
 | |
| #else
 | |
| 	/*
 | |
| 	 * FIXME: gp is set before it is initialized. If an XIP U-Boot ever
 | |
| 	 * encounters a pending IPI on boot it is liable to jump to whatever
 | |
| 	 * memory happens to be in ipi_data.addr on boot. It may also run into
 | |
| 	 * problems if it encounters an exception too early (because printf/puts
 | |
| 	 * accesses gd).
 | |
| 	 */
 | |
| 	mv	gp, s0
 | |
| 	bnez	tp, secondary_hart_loop
 | |
| #endif
 | |
| 
 | |
| #ifdef CONFIG_OF_PRIOR_STAGE
 | |
| 	la	t0, prior_stage_fdt_address
 | |
| 	SREG	s1, 0(t0)
 | |
| #endif
 | |
| 
 | |
| 	jal	board_init_f_init_reserve
 | |
| 
 | |
| 	SREG	s1, GD_FIRMWARE_FDT_ADDR(gp)
 | |
| 	/* save the boot hart id to global_data */
 | |
| 	SREG	tp, GD_BOOT_HART(gp)
 | |
| 
 | |
| #ifndef CONFIG_XIP
 | |
| 	la	t0, available_harts_lock
 | |
| 	amoswap.w.rl zero, zero, 0(t0)
 | |
| 
 | |
| wait_for_gd_init:
 | |
| 	la	t0, available_harts_lock
 | |
| 	li	t1, 1
 | |
| 1:	amoswap.w.aq t1, t1, 0(t0)
 | |
| 	bnez	t1, 1b
 | |
| 
 | |
| 	/*
 | |
| 	 * Set the global data pointer only when gd_t has been initialized.
 | |
| 	 * This was already set by arch_setup_gd on the boot hart, but all other
 | |
| 	 * harts' global data pointers gets set here.
 | |
| 	 */
 | |
| 	mv	gp, s0
 | |
| 
 | |
| 	/* register available harts in the available_harts mask */
 | |
| 	li	t1, 1
 | |
| 	sll	t1, t1, tp
 | |
| 	LREG	t2, GD_AVAILABLE_HARTS(gp)
 | |
| 	or	t2, t2, t1
 | |
| 	SREG	t2, GD_AVAILABLE_HARTS(gp)
 | |
| 
 | |
| 	amoswap.w.rl zero, zero, 0(t0)
 | |
| 
 | |
| 	/*
 | |
| 	 * Continue on hart lottery winner, others branch to
 | |
| 	 * secondary_hart_loop.
 | |
| 	 */
 | |
| 	bnez	s2, secondary_hart_loop
 | |
| #endif
 | |
| 
 | |
| 	/* Enable cache */
 | |
| 	jal	icache_enable
 | |
| 	jal	dcache_enable
 | |
| 
 | |
| #ifdef CONFIG_DEBUG_UART
 | |
| 	jal	debug_uart_init
 | |
| #endif
 | |
| 
 | |
| 	mv	a0, zero		/* a0 <-- boot_flags = 0 */
 | |
| 	la	t5, board_init_f
 | |
| 	jalr	t5			/* jump to board_init_f() */
 | |
| 
 | |
| #ifdef CONFIG_SPL_BUILD
 | |
| spl_clear_bss:
 | |
| 	la	t0, __bss_start
 | |
| 	la	t1, __bss_end
 | |
| 	beq	t0, t1, spl_stack_gd_setup
 | |
| 
 | |
| spl_clear_bss_loop:
 | |
| 	SREG	zero, 0(t0)
 | |
| 	addi	t0, t0, REGBYTES
 | |
| 	blt	t0, t1, spl_clear_bss_loop
 | |
| 
 | |
| spl_stack_gd_setup:
 | |
| 	jal	spl_relocate_stack_gd
 | |
| 
 | |
| 	/* skip setup if we did not relocate */
 | |
| 	beqz	a0, spl_call_board_init_r
 | |
| 	mv	s0, a0
 | |
| 
 | |
| 	/* setup stack on main hart */
 | |
| #if CONFIG_IS_ENABLED(SMP)
 | |
| 	/* tp: hart id */
 | |
| 	slli	t0, tp, CONFIG_STACK_SIZE_SHIFT
 | |
| 	sub	sp, s0, t0
 | |
| #else
 | |
| 	mv	sp, s0
 | |
| #endif
 | |
| 
 | |
| #if CONFIG_IS_ENABLED(SMP)
 | |
| 	/* set new stack and global data pointer on secondary harts */
 | |
| spl_secondary_hart_stack_gd_setup:
 | |
| 	la	a0, secondary_hart_relocate
 | |
| 	mv	a1, s0
 | |
| 	mv	a2, s0
 | |
| 	mv	a3, zero
 | |
| 	jal	smp_call_function
 | |
| 
 | |
| 	/* hang if relocation of secondary harts has failed */
 | |
| 	beqz	a0, 1f
 | |
| 	mv	a1, a0
 | |
| 	la	a0, secondary_harts_relocation_error
 | |
| 	jal	printf
 | |
| 	jal	hang
 | |
| #endif
 | |
| 
 | |
| 	/* set new global data pointer on main hart */
 | |
| 1:	mv	gp, s0
 | |
| 
 | |
| spl_call_board_init_r:
 | |
| 	mv	a0, zero
 | |
| 	mv	a1, zero
 | |
| 	jal	board_init_r
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|  * void relocate_code(addr_sp, gd, addr_moni)
 | |
|  *
 | |
|  * This "function" does not return, instead it continues in RAM
 | |
|  * after relocating the monitor code.
 | |
|  *
 | |
|  */
 | |
| .globl relocate_code
 | |
| relocate_code:
 | |
| 	mv	s2, a0			/* save addr_sp */
 | |
| 	mv	s3, a1			/* save addr of gd */
 | |
| 	mv	s4, a2			/* save addr of destination */
 | |
| 
 | |
| /*
 | |
|  *Set up the stack
 | |
|  */
 | |
| stack_setup:
 | |
| #if CONFIG_IS_ENABLED(SMP)
 | |
| 	/* tp: hart id */
 | |
| 	slli	t0, tp, CONFIG_STACK_SIZE_SHIFT
 | |
| 	sub	sp, s2, t0
 | |
| #else
 | |
| 	mv	sp, s2
 | |
| #endif
 | |
| 
 | |
| 	la	t0, _start
 | |
| 	sub	t6, s4, t0		/* t6 <- relocation offset */
 | |
| 	beq	t0, s4, clear_bss	/* skip relocation */
 | |
| 
 | |
| 	mv	t1, s4			/* t1 <- scratch for copy_loop */
 | |
| 	la	t3, __bss_start
 | |
| 	sub	t3, t3, t0		/* t3 <- __bss_start_ofs */
 | |
| 	add	t2, t0, t3		/* t2 <- source end address */
 | |
| 
 | |
| copy_loop:
 | |
| 	LREG	t5, 0(t0)
 | |
| 	addi	t0, t0, REGBYTES
 | |
| 	SREG	t5, 0(t1)
 | |
| 	addi	t1, t1, REGBYTES
 | |
| 	blt	t0, t2, copy_loop
 | |
| 
 | |
| /*
 | |
|  * Update dynamic relocations after board_init_f
 | |
|  */
 | |
| fix_rela_dyn:
 | |
| 	la	t1, __rel_dyn_start
 | |
| 	la	t2, __rel_dyn_end
 | |
| 	beq	t1, t2, clear_bss
 | |
| 	add	t1, t1, t6		/* t1 <- rela_dyn_start in RAM */
 | |
| 	add	t2, t2, t6		/* t2 <- rela_dyn_end in RAM */
 | |
| 
 | |
| /*
 | |
|  * skip first reserved entry: address, type, addend
 | |
|  */
 | |
| 	j	10f
 | |
| 
 | |
| 6:
 | |
| 	LREG	t5, -(REGBYTES*2)(t1)	/* t5 <-- relocation info:type */
 | |
| 	li	t3, R_RISCV_RELATIVE	/* reloc type R_RISCV_RELATIVE */
 | |
| 	bne	t5, t3, 8f		/* skip non-RISCV_RELOC entries */
 | |
| 	LREG	t3, -(REGBYTES*3)(t1)
 | |
| 	LREG	t5, -(REGBYTES)(t1)	/* t5 <-- addend */
 | |
| 	add	t5, t5, t6		/* t5 <-- location to fix up in RAM */
 | |
| 	add	t3, t3, t6		/* t3 <-- location to fix up in RAM */
 | |
| 	SREG	t5, 0(t3)
 | |
| 	j	10f
 | |
| 
 | |
| 8:
 | |
| 	la	t4, __dyn_sym_start
 | |
| 	add	t4, t4, t6
 | |
| 
 | |
| 9:
 | |
| 	LREG	t5, -(REGBYTES*2)(t1)	/* t5 <-- relocation info:type */
 | |
| 	srli	t0, t5, SYM_INDEX	/* t0 <--- sym table index */
 | |
| 	andi	t5, t5, 0xFF		/* t5 <--- relocation type */
 | |
| 	li	t3, RELOC_TYPE
 | |
| 	bne	t5, t3, 10f		/* skip non-addned entries */
 | |
| 
 | |
| 	LREG	t3, -(REGBYTES*3)(t1)
 | |
| 	li	t5, SYM_SIZE
 | |
| 	mul	t0, t0, t5
 | |
| 	add	s5, t4, t0
 | |
| 	LREG	t0, -(REGBYTES)(t1)	/* t0 <-- addend */
 | |
| 	LREG	t5, REGBYTES(s5)
 | |
| 	add	t5, t5, t0
 | |
| 	add	t5, t5, t6		/* t5 <-- location to fix up in RAM */
 | |
| 	add	t3, t3, t6		/* t3 <-- location to fix up in RAM */
 | |
| 	SREG	t5, 0(t3)
 | |
| 10:
 | |
| 	addi	t1, t1, (REGBYTES*3)
 | |
| 	ble	t1, t2, 6b
 | |
| 
 | |
| /*
 | |
|  * trap update
 | |
| */
 | |
| 	la	t0, trap_entry
 | |
| 	add	t0, t0, t6
 | |
| 	csrw	MODE_PREFIX(tvec), t0
 | |
| 
 | |
| clear_bss:
 | |
| 	la	t0, __bss_start		/* t0 <- rel __bss_start in FLASH */
 | |
| 	add	t0, t0, t6		/* t0 <- rel __bss_start in RAM */
 | |
| 	la	t1, __bss_end		/* t1 <- rel __bss_end in FLASH */
 | |
| 	add	t1, t1, t6		/* t1 <- rel __bss_end in RAM */
 | |
| 	beq	t0, t1, relocate_secondary_harts
 | |
| 
 | |
| clbss_l:
 | |
| 	SREG	zero, 0(t0)		/* clear loop... */
 | |
| 	addi	t0, t0, REGBYTES
 | |
| 	blt	t0, t1, clbss_l
 | |
| 
 | |
| relocate_secondary_harts:
 | |
| #if CONFIG_IS_ENABLED(SMP)
 | |
| 	/* send relocation IPI */
 | |
| 	la	t0, secondary_hart_relocate
 | |
| 	add	a0, t0, t6
 | |
| 
 | |
| 	/* store relocation offset */
 | |
| 	mv	s5, t6
 | |
| 
 | |
| 	mv	a1, s2
 | |
| 	mv	a2, s3
 | |
| 	mv	a3, zero
 | |
| 	jal	smp_call_function
 | |
| 
 | |
| 	/* hang if relocation of secondary harts has failed */
 | |
| 	beqz	a0, 1f
 | |
| 	mv	a1, a0
 | |
| 	la	a0, secondary_harts_relocation_error
 | |
| 	jal	printf
 | |
| 	jal	hang
 | |
| 
 | |
| 	/* restore relocation offset */
 | |
| 1:	mv	t6, s5
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|  * We are done. Do not return, instead branch to second part of board
 | |
|  * initialization, now running from RAM.
 | |
|  */
 | |
| call_board_init_r:
 | |
| 	jal	invalidate_icache_all
 | |
| 	jal	flush_dcache_all
 | |
| 	la	t0, board_init_r        /* offset of board_init_r() */
 | |
| 	add	t4, t0, t6		/* real address of board_init_r() */
 | |
| /*
 | |
|  * setup parameters for board_init_r
 | |
|  */
 | |
| 	mv	a0, s3			/* gd_t */
 | |
| 	mv	a1, s4			/* dest_addr */
 | |
| 
 | |
| /*
 | |
|  * jump to it ...
 | |
|  */
 | |
| 	jr	t4			/* jump to board_init_r() */
 | |
| 
 | |
| #if CONFIG_IS_ENABLED(SMP)
 | |
| hart_out_of_bounds_loop:
 | |
| 	/* Harts in this loop are out of bounds, increase CONFIG_NR_CPUS. */
 | |
| 	wfi
 | |
| 	j	hart_out_of_bounds_loop
 | |
| 
 | |
| /* SMP relocation entry */
 | |
| secondary_hart_relocate:
 | |
| 	/* a1: new sp */
 | |
| 	/* a2: new gd */
 | |
| 	/* tp: hart id */
 | |
| 
 | |
| 	/* setup stack */
 | |
| 	slli	t0, tp, CONFIG_STACK_SIZE_SHIFT
 | |
| 	sub	sp, a1, t0
 | |
| 
 | |
| 	/* update global data pointer */
 | |
| 	mv	gp, a2
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|  * Interrupts are disabled globally, but they can still be read from m/sip. The
 | |
|  * wfi function will wake us up if we get an IPI, even if we do not trap.
 | |
|  */
 | |
| secondary_hart_loop:
 | |
| 	wfi
 | |
| 
 | |
| #if CONFIG_IS_ENABLED(SMP)
 | |
| 	csrr	t0, MODE_PREFIX(ip)
 | |
| #if CONFIG_IS_ENABLED(RISCV_MMODE)
 | |
| 	andi	t0, t0, MIE_MSIE
 | |
| #else
 | |
| 	andi	t0, t0, SIE_SSIE
 | |
| #endif
 | |
| 	beqz	t0, secondary_hart_loop
 | |
| 
 | |
| 	mv	a0, tp
 | |
| 	jal	handle_ipi
 | |
| #endif
 | |
| 
 | |
| 	j	secondary_hart_loop
 |