blob: b7ee9b9c96b300e93022e0596e69e5d6c79c55d8 [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Boot entry point and assembler functions for riscv.
*
* Copyright (C) 2023, Ventana Micro Systems Inc., Andrew Jones <ajones@ventanamicro.com>
*/
#include <asm/asm.h>
#include <asm/asm-offsets.h>
#include <asm/csr.h>
.macro push_fp, ra=ra
addi sp, sp, -FP_SIZE
REG_S \ra, (FP_SIZE - SZREG)(sp)
REG_S fp, (FP_SIZE - 2*SZREG)(sp)
addi fp, sp, FP_SIZE
.endm
.macro pop_fp
REG_L ra, (FP_SIZE - SZREG)(sp)
REG_L fp, (FP_SIZE - 2*SZREG)(sp)
addi sp, sp, FP_SIZE
.endm
.macro zero_range, tmp1, tmp2
9998: beq \tmp1, \tmp2, 9997f
REG_S zero, 0(\tmp1)
addi \tmp1, \tmp1, 8
j 9998b
9997:
.endm
#ifdef CONFIG_EFI
#include "efi/crt0-efi-riscv64.S"
#else
.section .init
/*
* The hartid of the current core is in a0
* The address of the devicetree is in a1
*
* See Linux kernel doc Documentation/riscv/boot.rst
*/
.global start
start:
/*
* Stash the hartid in scratch and shift the dtb address into a0.
* thread_info_init() will later promote scratch to point at thread
* local storage.
*/
csrw CSR_SSCRATCH, a0
mv a0, a1
/*
* Update all R_RISCV_RELATIVE relocations using the table
* of Elf32_Rela/Elf64_Rela entries between reloc_start/end.
* The build will not emit other relocation types.
*/
la a1, reloc_start
la a2, reloc_end
la a3, start // base
1:
bge a1, a2, 1f
REG_L a4, ELF_RELA_OFFSET(a1) // r_offset
REG_L a5, ELF_RELA_ADDEND(a1) // r_addend
add a4, a3, a4 // addr = base + r_offset
add a5, a3, a5 // val = base + r_addend
REG_S a5, 0(a4) // *addr = val
addi a1, a1, ELF_RELA_SIZE
j 1b
1:
/* zero BSS */
la a1, bss
la a2, ebss
zero_range a1, a2
/* zero and set up stack */
la sp, stacktop
li a1, -8192
add a1, sp, a1
zero_range a1, sp
mv fp, zero // Ensure fp starts out as zero
/* set up exception handling */
la a1, exception_vectors
csrw CSR_STVEC, a1
/* complete setup */
la a1, stacktop // a1 is the base of free memory
mv a2, zero // clear a2 for xlen=32
call setup // a0 is the addr of the dtb
/* run the test */
la a0, __argc
lw a0, 0(a0)
la a1, __argv
la a2, __environ
call main
call exit
j halt
#endif /* !CONFIG_EFI */
.text
.balign 4
.global halt
halt:
1: wfi
j 1b
/*
* hartid_to_cpu
* a0 is a hartid on entry
* Returns, in a0, the corresponding cpuid, or -1 if no
* thread_info struct with 'hartid' is found.
*/
.balign 4
.global hartid_to_cpu
hartid_to_cpu:
la t0, cpus
la t1, nr_cpus
lw t1, 0(t1)
li t2, 0
1: bne t2, t1, 2f
li a0, -1
ret
2: REG_L t3, THREAD_INFO_HARTID(t0)
bne a0, t3, 3f
lw a0, THREAD_INFO_CPU(t0)
ret
3: addi t0, t0, THREAD_INFO_SIZE
addi t2, t2, 1
j 1b
.balign 4
.global secondary_entry
secondary_entry:
/*
* From the "HSM Hart Start Register State" table of the SBI spec:
* satp 0
* sstatus.SIE 0
* a0 hartid
* a1 opaque parameter
*
* __smp_boot_secondary() sets the opaque parameter (a1) to the physical
* address of the stack and the stack contains the secondary data.
*/
csrw CSR_SSCRATCH, a0
mv sp, a1
mv fp, zero
addi sp, sp, -SECONDARY_DATA_SIZE
REG_L a0, SECONDARY_STVEC(sp)
csrw CSR_STVEC, a0
mv a0, sp
call secondary_cinit
addi sp, sp, SECONDARY_DATA_SIZE
jalr ra, a0
call do_idle
j . /* unreachable */
/*
* Save context to address in a0.
* For a0, sets PT_A0(a0) to the contents of PT_ORIG_A0(a0).
* Clobbers a1.
*/
.macro save_context
REG_S ra, PT_RA(a0) // x1
REG_S sp, PT_SP(a0) // x2
REG_S gp, PT_GP(a0) // x3
REG_S tp, PT_TP(a0) // x4
REG_S t0, PT_T0(a0) // x5
REG_S t1, PT_T1(a0) // x6
REG_S t2, PT_T2(a0) // x7
REG_S s0, PT_S0(a0) // x8 / fp
REG_S s1, PT_S1(a0) // x9
/* a0 */ // x10
REG_S a1, PT_A1(a0) // x11
REG_S a2, PT_A2(a0) // x12
REG_S a3, PT_A3(a0) // x13
REG_S a4, PT_A4(a0) // x14
REG_S a5, PT_A5(a0) // x15
REG_S a6, PT_A6(a0) // x16
REG_S a7, PT_A7(a0) // x17
REG_S s2, PT_S2(a0) // x18
REG_S s3, PT_S3(a0) // x19
REG_S s4, PT_S4(a0) // x20
REG_S s5, PT_S5(a0) // x21
REG_S s6, PT_S6(a0) // x22
REG_S s7, PT_S7(a0) // x23
REG_S s8, PT_S8(a0) // x24
REG_S s9, PT_S9(a0) // x25
REG_S s10, PT_S10(a0) // x26
REG_S s11, PT_S11(a0) // x27
REG_S t3, PT_T3(a0) // x28
REG_S t4, PT_T4(a0) // x29
REG_S t5, PT_T5(a0) // x30
REG_S t6, PT_T6(a0) // x31
csrr a1, CSR_SEPC
REG_S a1, PT_EPC(a0)
csrr a1, CSR_SSTATUS
REG_S a1, PT_STATUS(a0)
csrr a1, CSR_STVAL
REG_S a1, PT_BADADDR(a0)
csrr a1, CSR_SCAUSE
REG_S a1, PT_CAUSE(a0)
REG_L a1, PT_ORIG_A0(a0)
REG_S a1, PT_A0(a0)
.endm
/*
* Restore context from address in a0.
* Also restores a0.
*/
.macro restore_context
REG_L ra, PT_RA(a0) // x1
REG_L sp, PT_SP(a0) // x2
REG_L gp, PT_GP(a0) // x3
REG_L tp, PT_TP(a0) // x4
REG_L t0, PT_T0(a0) // x5
REG_L t1, PT_T1(a0) // x6
REG_L t2, PT_T2(a0) // x7
REG_L s0, PT_S0(a0) // x8 / fp
REG_L s1, PT_S1(a0) // x9
/* a0 */ // x10
/* a1 */ // x11
REG_L a2, PT_A2(a0) // x12
REG_L a3, PT_A3(a0) // x13
REG_L a4, PT_A4(a0) // x14
REG_L a5, PT_A5(a0) // x15
REG_L a6, PT_A6(a0) // x16
REG_L a7, PT_A7(a0) // x17
REG_L s2, PT_S2(a0) // x18
REG_L s3, PT_S3(a0) // x19
REG_L s4, PT_S4(a0) // x20
REG_L s5, PT_S5(a0) // x21
REG_L s6, PT_S6(a0) // x22
REG_L s7, PT_S7(a0) // x23
REG_L s8, PT_S8(a0) // x24
REG_L s9, PT_S9(a0) // x25
REG_L s10, PT_S10(a0) // x26
REG_L s11, PT_S11(a0) // x27
REG_L t3, PT_T3(a0) // x28
REG_L t4, PT_T4(a0) // x29
REG_L t5, PT_T5(a0) // x30
REG_L t6, PT_T6(a0) // x31
REG_L a1, PT_EPC(a0)
csrw CSR_SEPC, a1
REG_L a1, PT_STATUS(a0)
csrw CSR_SSTATUS, a1
REG_L a1, PT_BADADDR(a0)
csrw CSR_STVAL, a1
REG_L a1, PT_CAUSE(a0)
csrw CSR_SCAUSE, a1
REG_L a1, PT_A1(a0)
REG_L a0, PT_A0(a0)
.endm
.balign 4
.global exception_vectors
exception_vectors:
REG_S a0, (-PT_SIZE - FP_SIZE + PT_ORIG_A0)(sp)
addi a0, sp, -PT_SIZE - FP_SIZE
save_context
/*
* Set a frame pointer "ra" which points to the last instruction.
* Add 1 to it, because pretty_print_stacks.py subtracts 1.
*/
REG_L a1, PT_EPC(a0)
addi a1, a1, 1
push_fp a1
mv sp, a0
call do_handle_exception
mv a0, sp
restore_context
sret