| /* |
| * SPDX-License-Identifier: BSD-2-Clause |
| * |
| * Copyright (c) 2019 Western Digital Corporation or its affiliates. |
| * |
| * Authors: |
| * Anup Patel <anup.patel@wdc.com> |
| */ |
| |
| #include <sbi/riscv_asm.h> |
| #include <sbi/riscv_encoding.h> |
| #include <sbi/sbi_platform.h> |
| #include <sbi/sbi_scratch.h> |
| #include <sbi/sbi_trap.h> |
| |
| .align 3 |
| .section .entry, "ax", %progbits |
| .globl _start |
| .globl _start_warm |
| _start: |
| /* |
| * Jump to warm-boot if this is not the first core booting, |
| * that is, for mhartid != 0 |
| */ |
| csrr a6, mhartid |
| blt zero, a6, _wait_for_boot_hart |
| |
| /* Zero-out BSS */ |
| la a4, _bss_start |
| la a5, _bss_end |
| _bss_zero: |
| REG_S zero, (a4) |
| add a4, a4, __SIZEOF_POINTER__ |
| blt a4, a5, _bss_zero |
| |
| /* Override pervious arg1 */ |
| add s0, a0, zero |
| add s1, a1, zero |
| call fw_prev_arg1 |
| add t1, a0, zero |
| add a0, s0, zero |
| add a1, s1, zero |
| beqz t1, _prev_arg1_override_done |
| add a1, t1, zero |
| _prev_arg1_override_done: |
| |
| /* |
| * Relocate Flatened Device Tree (FDT) |
| * source FDT address = previous arg1 |
| * destination FDT address = next arg1 |
| * |
| * Note: We will preserve a0 and a1 passed by |
| * previous booting stage. |
| */ |
| beqz a1, _fdt_reloc_done |
| /* Mask values in a3 and a4 */ |
| li a3, ~0xf |
| li a4, 0xff |
| /* t1 = destination FDT start address */ |
| add s0, a0, zero |
| add s1, a1, zero |
| call fw_next_arg1 |
| add t1, a0, zero |
| add a0, s0, zero |
| add a1, s1, zero |
| beqz t1, _fdt_reloc_done |
| and t1, t1, a3 |
| /* t0 = source FDT start address */ |
| add t0, a1, zero |
| and t0, t0, a3 |
| /* t2 = source FDT size in big-endian */ |
| lwu t2, 4(t0) |
| /* t3 = bit[15:8] of FDT size */ |
| add t3, t2, zero |
| srli t3, t3, 16 |
| and t3, t3, a4 |
| slli t3, t3, 8 |
| /* t4 = bit[23:16] of FDT size */ |
| add t4, t2, zero |
| srli t4, t4, 8 |
| and t4, t4, a4 |
| slli t4, t4, 16 |
| /* t5 = bit[31:24] of FDT size */ |
| add t5, t2, zero |
| and t5, t5, a4 |
| slli t5, t5, 24 |
| /* t2 = bit[7:0] of FDT size */ |
| srli t2, t2, 24 |
| and t2, t2, a4 |
| /* t2 = FDT size in little-endian */ |
| or t2, t2, t3 |
| or t2, t2, t4 |
| or t2, t2, t5 |
| /* t2 = destination FDT end address */ |
| add t2, t1, t2 |
| /* FDT copy loop */ |
| ble t2, t1, _fdt_reloc_done |
| _fdt_reloc_again: |
| REG_L t3, 0(t0) |
| REG_S t3, 0(t1) |
| add t0, t0, __SIZEOF_POINTER__ |
| add t1, t1, __SIZEOF_POINTER__ |
| blt t1, t2, _fdt_reloc_again |
| _fdt_reloc_done: |
| |
| /* Update boot hart flag */ |
| la a4, _boot_hart_done |
| li a5, 1 |
| REG_S a5, (a4) |
| j _wait_for_boot_hart |
| |
| .align 3 |
| _boot_hart_done: |
| RISCV_PTR 0 |
| .align 3 |
| |
| /* Wait for boot hart */ |
| _wait_for_boot_hart: |
| la a4, _boot_hart_done |
| REG_L a5, (a4) |
| beqz a5, _wait_for_boot_hart |
| |
| _start_warm: |
| /* Disable and clear all interrupts */ |
| csrw mie, zero |
| csrw mip, zero |
| |
| /* Preload per-HART details |
| * s6 -> HART ID |
| * s7 -> HART Count |
| * s8 -> HART Stack Size |
| */ |
| csrr s6, mhartid |
| la a4, platform |
| lwu s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4) |
| lwu s8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4) |
| |
| /* HART ID should be within expected limit */ |
| csrr s6, mhartid |
| bge s6, s7, _start_hang |
| |
| /* Setup scratch space */ |
| la tp, _fw_end |
| mul a5, s7, s8 |
| add tp, tp, a5 |
| mul a5, s8, s6 |
| sub tp, tp, a5 |
| li a5, SBI_SCRATCH_SIZE |
| sub tp, tp, a5 |
| csrw mscratch, tp |
| |
| /* Initialize scratch space */ |
| la a4, _fw_start |
| la a5, _fw_end |
| mul t0, s7, s8 |
| add a5, a5, t0 |
| sub a5, a5, a4 |
| REG_S a4, SBI_SCRATCH_FW_START_OFFSET(tp) |
| REG_S a5, SBI_SCRATCH_FW_SIZE_OFFSET(tp) |
| /* Note: fw_next_arg1() uses a0, a1, and ra */ |
| call fw_next_arg1 |
| REG_S a0, SBI_SCRATCH_NEXT_ARG1_OFFSET(tp) |
| /* Note: fw_next_addr() uses a0, a1, and ra */ |
| call fw_next_addr |
| REG_S a0, SBI_SCRATCH_NEXT_ADDR_OFFSET(tp) |
| li a4, PRV_S |
| REG_S a4, SBI_SCRATCH_NEXT_MODE_OFFSET(tp) |
| la a4, _start_warm |
| REG_S a4, SBI_SCRATCH_WARMBOOT_ADDR_OFFSET(tp) |
| la a4, platform |
| REG_S a4, SBI_SCRATCH_PLATFORM_ADDR_OFFSET(tp) |
| la a4, _hartid_to_scratch |
| REG_S a4, SBI_SCRATCH_HARTID_TO_SCRATCH_OFFSET(tp) |
| REG_S zero, SBI_SCRATCH_IPI_TYPE_OFFSET(tp) |
| |
| /* Setup stack */ |
| add sp, tp, zero |
| |
| /* Setup trap handler */ |
| la a4, _trap_handler |
| csrw mtvec, a4 |
| |
| /* Initialize SBI runtime */ |
| csrr a0, mscratch |
| call sbi_init |
| |
| /* We don't expect to reach here hence just hang */ |
| j _start_hang |
| |
| .align 3 |
| .section .entry, "ax", %progbits |
| .globl _hartid_to_scratch |
| _hartid_to_scratch: |
| add sp, sp, -(3 * __SIZEOF_POINTER__) |
| REG_S s0, (sp) |
| REG_S s1, (__SIZEOF_POINTER__)(sp) |
| REG_S s2, (__SIZEOF_POINTER__ * 2)(sp) |
| /* |
| * a0 -> HART ID (passed by caller) |
| * s0 -> HART Stack Size |
| * s1 -> HART Stack End |
| * s2 -> Temporary |
| */ |
| la s2, platform |
| lwu s0, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(s2) |
| lwu s2, SBI_PLATFORM_HART_COUNT_OFFSET(s2) |
| mul s2, s2, s0 |
| la s1, _fw_end |
| add s1, s1, s2 |
| mul s2, s0, a0 |
| sub s1, s1, s2 |
| li s2, SBI_SCRATCH_SIZE |
| sub a0, s1, s2 |
| REG_L s0, (sp) |
| REG_L s1, (__SIZEOF_POINTER__)(sp) |
| REG_L s2, (__SIZEOF_POINTER__ * 2)(sp) |
| add sp, sp, (3 * __SIZEOF_POINTER__) |
| ret |
| |
| .align 3 |
| .section .entry, "ax", %progbits |
| .globl _start_hang |
| _start_hang: |
| wfi |
| j _start_hang |
| |
| .align 3 |
| .section .entry, "ax", %progbits |
| .globl _trap_handler |
| _trap_handler: |
| /* Swap SP and MSCRATCH */ |
| csrrw sp, mscratch, sp |
| |
| /* Setup exception stack */ |
| add sp, sp, -(SBI_TRAP_REGS_SIZE) |
| |
| /* Save RA, T0, T1, and T2 */ |
| REG_S ra, SBI_TRAP_REGS_OFFSET(ra)(sp) |
| REG_S t0, SBI_TRAP_REGS_OFFSET(t0)(sp) |
| REG_S t1, SBI_TRAP_REGS_OFFSET(t1)(sp) |
| REG_S t2, SBI_TRAP_REGS_OFFSET(t2)(sp) |
| |
| /* Save original SP and restore MSCRATCH */ |
| add t0, sp, SBI_TRAP_REGS_SIZE |
| csrrw t0, mscratch, t0 |
| REG_S t0, SBI_TRAP_REGS_OFFSET(sp)(sp) |
| |
| /* Save MEPC and MSTATUS CSRs */ |
| csrr t0, mepc |
| csrr t1, mstatus |
| |
| /* |
| * Note: Fast path trap handling can be done here |
| * using SP, RA, T0, T1, and T2 registers where |
| * T0 <- MEPC |
| * T1 <- MSTATUS |
| */ |
| |
| /* Save MEPC and MSTATUS CSRs */ |
| REG_S t0, SBI_TRAP_REGS_OFFSET(mepc)(sp) |
| REG_S t1, SBI_TRAP_REGS_OFFSET(mstatus)(sp) |
| |
| /* Save all general regisers except SP, RA, T0, T1, and T2 */ |
| REG_S zero, SBI_TRAP_REGS_OFFSET(zero)(sp) |
| REG_S gp, SBI_TRAP_REGS_OFFSET(gp)(sp) |
| REG_S tp, SBI_TRAP_REGS_OFFSET(tp)(sp) |
| REG_S s0, SBI_TRAP_REGS_OFFSET(s0)(sp) |
| REG_S s1, SBI_TRAP_REGS_OFFSET(s1)(sp) |
| REG_S a0, SBI_TRAP_REGS_OFFSET(a0)(sp) |
| REG_S a1, SBI_TRAP_REGS_OFFSET(a1)(sp) |
| REG_S a2, SBI_TRAP_REGS_OFFSET(a2)(sp) |
| REG_S a3, SBI_TRAP_REGS_OFFSET(a3)(sp) |
| REG_S a4, SBI_TRAP_REGS_OFFSET(a4)(sp) |
| REG_S a5, SBI_TRAP_REGS_OFFSET(a5)(sp) |
| REG_S a6, SBI_TRAP_REGS_OFFSET(a6)(sp) |
| REG_S a7, SBI_TRAP_REGS_OFFSET(a7)(sp) |
| REG_S s2, SBI_TRAP_REGS_OFFSET(s2)(sp) |
| REG_S s3, SBI_TRAP_REGS_OFFSET(s3)(sp) |
| REG_S s4, SBI_TRAP_REGS_OFFSET(s4)(sp) |
| REG_S s5, SBI_TRAP_REGS_OFFSET(s5)(sp) |
| REG_S s6, SBI_TRAP_REGS_OFFSET(s6)(sp) |
| REG_S s7, SBI_TRAP_REGS_OFFSET(s7)(sp) |
| REG_S s8, SBI_TRAP_REGS_OFFSET(s8)(sp) |
| REG_S s9, SBI_TRAP_REGS_OFFSET(s9)(sp) |
| REG_S s10, SBI_TRAP_REGS_OFFSET(s10)(sp) |
| REG_S s11, SBI_TRAP_REGS_OFFSET(s11)(sp) |
| REG_S t3, SBI_TRAP_REGS_OFFSET(t3)(sp) |
| REG_S t4, SBI_TRAP_REGS_OFFSET(t4)(sp) |
| REG_S t5, SBI_TRAP_REGS_OFFSET(t5)(sp) |
| REG_S t6, SBI_TRAP_REGS_OFFSET(t6)(sp) |
| |
| /* Call C routine */ |
| add a0, sp, zero |
| csrr a1, mscratch |
| call sbi_trap_handler |
| |
| /* Restore all general regisers except SP, RA, T0, T1, T2, and T3 */ |
| REG_L gp, SBI_TRAP_REGS_OFFSET(gp)(sp) |
| REG_L tp, SBI_TRAP_REGS_OFFSET(tp)(sp) |
| REG_L s0, SBI_TRAP_REGS_OFFSET(s0)(sp) |
| REG_L s1, SBI_TRAP_REGS_OFFSET(s1)(sp) |
| REG_L a0, SBI_TRAP_REGS_OFFSET(a0)(sp) |
| REG_L a1, SBI_TRAP_REGS_OFFSET(a1)(sp) |
| REG_L a2, SBI_TRAP_REGS_OFFSET(a2)(sp) |
| REG_L a3, SBI_TRAP_REGS_OFFSET(a3)(sp) |
| REG_L a4, SBI_TRAP_REGS_OFFSET(a4)(sp) |
| REG_L a5, SBI_TRAP_REGS_OFFSET(a5)(sp) |
| REG_L a6, SBI_TRAP_REGS_OFFSET(a6)(sp) |
| REG_L a7, SBI_TRAP_REGS_OFFSET(a7)(sp) |
| REG_L s2, SBI_TRAP_REGS_OFFSET(s2)(sp) |
| REG_L s3, SBI_TRAP_REGS_OFFSET(s3)(sp) |
| REG_L s4, SBI_TRAP_REGS_OFFSET(s4)(sp) |
| REG_L s5, SBI_TRAP_REGS_OFFSET(s5)(sp) |
| REG_L s6, SBI_TRAP_REGS_OFFSET(s6)(sp) |
| REG_L s7, SBI_TRAP_REGS_OFFSET(s7)(sp) |
| REG_L s8, SBI_TRAP_REGS_OFFSET(s8)(sp) |
| REG_L s9, SBI_TRAP_REGS_OFFSET(s9)(sp) |
| REG_L s10, SBI_TRAP_REGS_OFFSET(s10)(sp) |
| REG_L s11, SBI_TRAP_REGS_OFFSET(s11)(sp) |
| REG_L t3, SBI_TRAP_REGS_OFFSET(t3)(sp) |
| REG_L t4, SBI_TRAP_REGS_OFFSET(t4)(sp) |
| REG_L t5, SBI_TRAP_REGS_OFFSET(t5)(sp) |
| REG_L t6, SBI_TRAP_REGS_OFFSET(t6)(sp) |
| |
| /* Load T0 and T1 with MEPC and MSTATUS */ |
| REG_L t0, SBI_TRAP_REGS_OFFSET(mepc)(sp) |
| REG_L t1, SBI_TRAP_REGS_OFFSET(mstatus)(sp) |
| |
| /* |
| * Note: Jump here after fast trap handling |
| * using SP, RA, T0, T1, and T2 |
| * T0 <- MEPC |
| * T1 <- MSTATUS |
| */ |
| |
| /* Restore MEPC and MSTATUS CSRs */ |
| csrw mepc, t0 |
| csrw mstatus, t1 |
| |
| /* Restore RA, T0, T1, and T2 */ |
| REG_L ra, SBI_TRAP_REGS_OFFSET(ra)(sp) |
| REG_L t0, SBI_TRAP_REGS_OFFSET(t0)(sp) |
| REG_L t1, SBI_TRAP_REGS_OFFSET(t1)(sp) |
| REG_L t2, SBI_TRAP_REGS_OFFSET(t2)(sp) |
| |
| /* Restore SP */ |
| REG_L sp, SBI_TRAP_REGS_OFFSET(sp)(sp) |
| |
| mret |