mirror of
https://github.com/memtest86plus/memtest86plus.git
synced 2026-08-26 21:27:15 -05:00
449 lines
11 KiB
ArmAsm
449 lines
11 KiB
ArmAsm
// SPDX-License-Identifier: GPL-2.0
|
|
//
|
|
// startup64.S contains the 64-bit startup code for both the BSP and APs.
|
|
// It initialises stacks, memory management, and exception handling, clears
|
|
// the BSS, completes relocation, and finally calls the main application.
|
|
//
|
|
// Copyright (C) 2020-2022 Martin Whitaker.
|
|
// Copyright (C) 2024 Loongson Technology Corporation Limited. All rights reserved.
|
|
//
|
|
|
|
#define __ASSEMBLY__
|
|
|
|
#include "boot.h"
|
|
#inlucde "interrupt.h"
|
|
#include "registers.h"
|
|
|
|
.text
|
|
|
|
exception_entry:
|
|
csrwr $sp, LOONGARCH_CSR_KS0
|
|
|
|
csrrd $sp, LOONGARCH_CSR_KS0
|
|
|
|
//
|
|
// Push GP registers
|
|
//
|
|
addi.d $sp, $sp, -(GP_REG_CONTEXT_SIZE + FP_REG_CONTEXT_SIZE + CSR_REG_CONTEXT_SIZE)
|
|
st.d $zero, $sp, 0 * RSIZE
|
|
st.d $ra, $sp, 1 * RSIZE
|
|
st.d $tp, $sp, 2 * RSIZE
|
|
st.d $a0, $sp, 4 * RSIZE
|
|
st.d $a1, $sp, 5 * RSIZE
|
|
st.d $a2, $sp, 6 * RSIZE
|
|
st.d $a3, $sp, 7 * RSIZE
|
|
st.d $a4, $sp, 8 * RSIZE
|
|
st.d $a5, $sp, 9 * RSIZE
|
|
st.d $a6, $sp, 10 * RSIZE
|
|
st.d $a7, $sp, 11 * RSIZE
|
|
st.d $t0, $sp, 12 * RSIZE
|
|
st.d $t1, $sp, 13 * RSIZE
|
|
st.d $t2, $sp, 14 * RSIZE
|
|
st.d $t3, $sp, 15 * RSIZE
|
|
st.d $t4, $sp, 16 * RSIZE
|
|
st.d $t5, $sp, 17 * RSIZE
|
|
st.d $t6, $sp, 18 * RSIZE
|
|
st.d $t7, $sp, 19 * RSIZE
|
|
st.d $t8, $sp, 20 * RSIZE
|
|
st.d $r21, $sp, 21 * RSIZE
|
|
st.d $fp, $sp, 22 * RSIZE
|
|
st.d $s0, $sp, 23 * RSIZE
|
|
st.d $s1, $sp, 24 * RSIZE
|
|
st.d $s2, $sp, 25 * RSIZE
|
|
st.d $s3, $sp, 26 * RSIZE
|
|
st.d $s4, $sp, 27 * RSIZE
|
|
st.d $s5, $sp, 28 * RSIZE
|
|
st.d $s6, $sp, 29 * RSIZE
|
|
st.d $s7, $sp, 30 * RSIZE
|
|
st.d $s8, $sp, 31 * RSIZE
|
|
csrrd $t0, LOONGARCH_CSR_KS0 // Read the old stack pointer.
|
|
st.d $t0, $sp, 3 * RSIZE
|
|
|
|
//
|
|
// Push CSR registers
|
|
//
|
|
addi.d $sp, $sp, GP_REG_CONTEXT_SIZE
|
|
|
|
csrrd $t0, LOONGARCH_CSR_CRMD
|
|
st.d $t0, $sp, LOONGARCH_CSR_CRMD * RSIZE
|
|
csrrd $t0, LOONGARCH_CSR_PRMD
|
|
st.d $t0, $sp, LOONGARCH_CSR_PRMD * RSIZE
|
|
csrrd $t0, LOONGARCH_CSR_EUEN
|
|
st.d $t0, $sp, LOONGARCH_CSR_EUEN * RSIZE
|
|
csrrd $t0, LOONGARCH_CSR_MISC
|
|
st.d $t0, $sp, LOONGARCH_CSR_MISC * RSIZE
|
|
csrrd $t0, LOONGARCH_CSR_ECFG
|
|
st.d $t0, $sp, LOONGARCH_CSR_ECFG * RSIZE
|
|
csrrd $t0, LOONGARCH_CSR_ESTAT
|
|
st.d $t0, $sp, LOONGARCH_CSR_ESTAT * RSIZE
|
|
csrrd $t0, LOONGARCH_CSR_ERA
|
|
st.d $t0, $sp, LOONGARCH_CSR_ERA * RSIZE
|
|
csrrd $t0, LOONGARCH_CSR_BADV
|
|
st.d $t0, $sp, LOONGARCH_CSR_BADV * RSIZE
|
|
csrrd $t0, LOONGARCH_CSR_BADI
|
|
st.d $t0, $sp, LOONGARCH_CSR_BADI * RSIZE
|
|
|
|
//
|
|
// Push FP registers
|
|
//
|
|
addi.d $sp, $sp, CSR_REG_CONTEXT_SIZE
|
|
|
|
csrrd $t0, LOONGARCH_CSR_EUEN
|
|
andi $t0, $t0, 0x1
|
|
beqz $t0, PushRegDone
|
|
|
|
fst.d $fa0, $sp, 0 * RSIZE
|
|
fst.d $fa1, $sp, 1 * RSIZE
|
|
fst.d $fa2, $sp, 2 * RSIZE
|
|
fst.d $fa3, $sp, 3 * RSIZE
|
|
fst.d $fa4, $sp, 4 * RSIZE
|
|
fst.d $fa5, $sp, 5 * RSIZE
|
|
fst.d $fa6, $sp, 6 * RSIZE
|
|
fst.d $fa7, $sp, 7 * RSIZE
|
|
fst.d $ft0, $sp, 8 * RSIZE
|
|
fst.d $ft1, $sp, 9 * RSIZE
|
|
fst.d $ft2, $sp, 10 * RSIZE
|
|
fst.d $ft3, $sp, 11 * RSIZE
|
|
fst.d $ft4, $sp, 12 * RSIZE
|
|
fst.d $ft5, $sp, 13 * RSIZE
|
|
fst.d $ft6, $sp, 14 * RSIZE
|
|
fst.d $ft7, $sp, 15 * RSIZE
|
|
fst.d $ft8, $sp, 16 * RSIZE
|
|
fst.d $ft9, $sp, 17 * RSIZE
|
|
fst.d $ft10, $sp, 18 * RSIZE
|
|
fst.d $ft11, $sp, 19 * RSIZE
|
|
fst.d $ft12, $sp, 20 * RSIZE
|
|
fst.d $ft13, $sp, 21 * RSIZE
|
|
fst.d $ft14, $sp, 22 * RSIZE
|
|
fst.d $ft15, $sp, 23 * RSIZE
|
|
fst.d $fs0, $sp, 24 * RSIZE
|
|
fst.d $fs1, $sp, 25 * RSIZE
|
|
fst.d $fs2, $sp, 26 * RSIZE
|
|
fst.d $fs3, $sp, 27 * RSIZE
|
|
fst.d $fs4, $sp, 28 * RSIZE
|
|
fst.d $fs5, $sp, 29 * RSIZE
|
|
fst.d $fs6, $sp, 30 * RSIZE
|
|
fst.d $fs7, $sp, 31 * RSIZE
|
|
|
|
movfcsr2gr $t3, $fcsr0
|
|
st.d $t3, $sp, 32 * RSIZE // Push the FCSR0 register.
|
|
|
|
//
|
|
// Push the fcc0-fcc7 registers.
|
|
//
|
|
movcf2gr $t3, $fcc0
|
|
move $t2, $t3
|
|
movcf2gr $t3, $fcc1
|
|
bstrins.d $t2, $t3, 0xf, 0x8
|
|
movcf2gr $t3, $fcc2
|
|
bstrins.d $t2, $t3, 0x17, 0x10
|
|
movcf2gr $t3, $fcc3
|
|
bstrins.d $t2, $t3, 0x1f, 0x18
|
|
movcf2gr $t3, $fcc4
|
|
bstrins.d $t2, $t3, 0x27, 0x20
|
|
movcf2gr $t3, $fcc5
|
|
bstrins.d $t2, $t3, 0x2f, 0x28
|
|
movcf2gr $t3, $fcc6
|
|
bstrins.d $t2, $t3, 0x37, 0x30
|
|
movcf2gr $t3, $fcc7
|
|
bstrins.d $t2, $t3, 0x3f, 0x38
|
|
st.d $t2, $sp, 33 * RSIZE
|
|
//
|
|
// Push exception context down
|
|
//
|
|
|
|
PushRegDone:
|
|
addi.d $sp, $sp, -(GP_REG_CONTEXT_SIZE + CSR_REG_CONTEXT_SIZE)
|
|
move $a0, $sp
|
|
bl interrupt
|
|
|
|
# If returned, POP the REG
|
|
|
|
//
|
|
// Pop CSR reigsters
|
|
//
|
|
addi.d $sp, $sp, GP_REG_CONTEXT_SIZE
|
|
|
|
ld.d $t0, $sp, LOONGARCH_CSR_CRMD * RSIZE
|
|
csrwr $t0, LOONGARCH_CSR_CRMD
|
|
ld.d $t0, $sp, LOONGARCH_CSR_PRMD * RSIZE
|
|
csrwr $t0, LOONGARCH_CSR_PRMD
|
|
ld.d $t0, $sp, LOONGARCH_CSR_ECFG * RSIZE
|
|
csrwr $t0, LOONGARCH_CSR_ECFG
|
|
ld.d $t0, $sp, LOONGARCH_CSR_ERA * RSIZE
|
|
csrwr $t0, LOONGARCH_CSR_ERA
|
|
|
|
addi.d $sp, $sp, CSR_REG_CONTEXT_SIZE // Fource change the stack pointer befor pop the FP registers.
|
|
|
|
csrrd $t1, LOONGARCH_CSR_EUEN
|
|
andi $t1, $t1, 0x1
|
|
beqz $t1, PopGP // If the FPE not set, only pop the GP registers.
|
|
|
|
//
|
|
// Pop FP registers
|
|
//
|
|
fld.d $fa0, $sp, 0 * RSIZE
|
|
fld.d $fa1, $sp, 1 * RSIZE
|
|
fld.d $fa2, $sp, 2 * RSIZE
|
|
fld.d $fa3, $sp, 3 * RSIZE
|
|
fld.d $fa4, $sp, 4 * RSIZE
|
|
fld.d $fa5, $sp, 5 * RSIZE
|
|
fld.d $fa6, $sp, 6 * RSIZE
|
|
fld.d $fa7, $sp, 7 * RSIZE
|
|
fld.d $ft0, $sp, 8 * RSIZE
|
|
fld.d $ft1, $sp, 9 * RSIZE
|
|
fld.d $ft2, $sp, 10 * RSIZE
|
|
fld.d $ft3, $sp, 11 * RSIZE
|
|
fld.d $ft4, $sp, 12 * RSIZE
|
|
fld.d $ft5, $sp, 13 * RSIZE
|
|
fld.d $ft6, $sp, 14 * RSIZE
|
|
fld.d $ft7, $sp, 15 * RSIZE
|
|
fld.d $ft8, $sp, 16 * RSIZE
|
|
fld.d $ft9, $sp, 17 * RSIZE
|
|
fld.d $ft10, $sp, 18 * RSIZE
|
|
fld.d $ft11, $sp, 19 * RSIZE
|
|
fld.d $ft12, $sp, 20 * RSIZE
|
|
fld.d $ft13, $sp, 21 * RSIZE
|
|
fld.d $ft14, $sp, 22 * RSIZE
|
|
fld.d $ft15, $sp, 23 * RSIZE
|
|
fld.d $fs0, $sp, 24 * RSIZE
|
|
fld.d $fs1, $sp, 25 * RSIZE
|
|
fld.d $fs2, $sp, 26 * RSIZE
|
|
fld.d $fs3, $sp, 27 * RSIZE
|
|
fld.d $fs4, $sp, 28 * RSIZE
|
|
fld.d $fs5, $sp, 29 * RSIZE
|
|
fld.d $fs6, $sp, 30 * RSIZE
|
|
fld.d $fs7, $sp, 31 * RSIZE
|
|
|
|
ld.d $t0, $sp, 32 * RSIZE
|
|
movgr2fcsr $fcsr0, $t0 // Pop the fcsr0 register.
|
|
|
|
//
|
|
// Pop the fcc0-fcc7 registers.
|
|
//
|
|
ld.d $t0, $sp, 33 * RSIZE
|
|
bstrpick.d $t1, $t0, 7, 0
|
|
movgr2cf $fcc0, $t1
|
|
bstrpick.d $t1, $t0, 15, 8
|
|
movgr2cf $fcc1, $t1
|
|
bstrpick.d $t1, $t0, 23, 16
|
|
movgr2cf $fcc2, $t1
|
|
bstrpick.d $t1, $t0, 31, 24
|
|
movgr2cf $fcc3, $t1
|
|
bstrpick.d $t1, $t0, 39, 32
|
|
movgr2cf $fcc4, $t1
|
|
bstrpick.d $t1, $t0, 47, 40
|
|
movgr2cf $fcc5, $t1
|
|
bstrpick.d $t1, $t0, 55, 48
|
|
movgr2cf $fcc6, $t1
|
|
bstrpick.d $t1, $t0, 63, 56
|
|
movgr2cf $fcc7, $t1
|
|
|
|
PopGP:
|
|
|
|
//
|
|
// Pop GP registers
|
|
//
|
|
addi.d $sp, $sp, -(GP_REG_CONTEXT_SIZE + CSR_REG_CONTEXT_SIZE)
|
|
|
|
ld.d $ra, $sp, 1 * RSIZE
|
|
ld.d $tp, $sp, 2 * RSIZE
|
|
ld.d $a0, $sp, 4 * RSIZE
|
|
ld.d $a1, $sp, 5 * RSIZE
|
|
ld.d $a2, $sp, 6 * RSIZE
|
|
ld.d $a3, $sp, 7 * RSIZE
|
|
ld.d $a4, $sp, 8 * RSIZE
|
|
ld.d $a5, $sp, 9 * RSIZE
|
|
ld.d $a6, $sp, 10 * RSIZE
|
|
ld.d $a7, $sp, 11 * RSIZE
|
|
ld.d $t0, $sp, 12 * RSIZE
|
|
ld.d $t1, $sp, 13 * RSIZE
|
|
ld.d $t2, $sp, 14 * RSIZE
|
|
ld.d $t3, $sp, 15 * RSIZE
|
|
ld.d $t4, $sp, 16 * RSIZE
|
|
ld.d $t5, $sp, 17 * RSIZE
|
|
ld.d $t6, $sp, 18 * RSIZE
|
|
ld.d $t7, $sp, 19 * RSIZE
|
|
ld.d $t8, $sp, 20 * RSIZE
|
|
ld.d $r21, $sp, 21 * RSIZE
|
|
ld.d $fp, $sp, 22 * RSIZE
|
|
ld.d $s0, $sp, 23 * RSIZE
|
|
ld.d $s1, $sp, 24 * RSIZE
|
|
ld.d $s2, $sp, 25 * RSIZE
|
|
ld.d $s3, $sp, 26 * RSIZE
|
|
ld.d $s4, $sp, 27 * RSIZE
|
|
ld.d $s5, $sp, 28 * RSIZE
|
|
ld.d $s6, $sp, 29 * RSIZE
|
|
ld.d $s7, $sp, 30 * RSIZE
|
|
ld.d $s8, $sp, 31 * RSIZE
|
|
ld.d $sp, $sp, 3 * RSIZE
|
|
|
|
ertn // Return from exception.
|
|
|
|
.globl startup32
|
|
|
|
startup32:
|
|
break 0 # Should be unreachable.
|
|
|
|
# The EFI PE32+ boot entry point. The entry point for MP boot.
|
|
|
|
.org 0x400
|
|
.globl efi_boot
|
|
efi_boot:
|
|
move $a2, $zero # the boot params pointer (0 = not yet allocated)
|
|
|
|
bl efi_setup
|
|
|
|
# Save the boot params pointer.
|
|
la.pcrel $t0, boot_params_addr
|
|
st.d $a0, $t0, 0x0
|
|
b startup64
|
|
|
|
# The 64-bit boot entry point and the entry point for AP boot.
|
|
|
|
.org 0x440
|
|
.globl startup64
|
|
startup64:
|
|
# Disable intrrupts globally
|
|
li.w $t0, (0x1 << 2)
|
|
csrxchg $zero, $t0, LOONGARCH_CSR_CRMD
|
|
|
|
# Init Core
|
|
li.w $t0, 0xA8 # DA mode, load/store cacheable, instructions cacheable, interrupts disabled, DMWn has no effect.
|
|
csrwr $t0, LOONGARCH_CSR_CRMD
|
|
|
|
//
|
|
// Make sure the legacy boot mode works properly.
|
|
//
|
|
la.pcrel $t0, Jmp
|
|
bstrins.d $t0, $zero, 63, 32
|
|
jr $t0
|
|
|
|
Jmp:
|
|
invtlb 0x0, $zero, $zero
|
|
|
|
# Set the PG and DMWn
|
|
li.d $t0, 0x8000000000000001
|
|
csrwr $t0, LOONGARCH_CSR_DMWIN0 # Uncache able address window, MMIO base: 0x8000000000000000
|
|
li.d $t0, 0x11 # Cache able address window, PA == VA
|
|
csrwr $t0, LOONGARCH_CSR_DMWIN1
|
|
|
|
li.w $t0, 0xB0 # PG mode, load/store cacheable, instructions cacheable, disable intrrupts, DMWn effected.
|
|
csrwr $t0, LOONGARCH_CSR_CRMD
|
|
|
|
# Enable all IPIs
|
|
li.w $t0, 0xFFFFFFFF
|
|
li.w $t1, 0x1004
|
|
iocsrwr.w $t0, $t1
|
|
|
|
# Turn on the FPU, plus LSX/LASX when implemented
|
|
# (CPUCFG2 bits 6/7 map directly onto EUEN.SXE/ASXE at bits 1/2).
|
|
li.w $t0, 0x2
|
|
cpucfg $t0, $t0
|
|
andi $t0, $t0, 0xC0
|
|
srli.w $t0, $t0, 5
|
|
ori $t0, $t0, 0x1
|
|
csrxchg $t0, $t0, LOONGARCH_CSR_EUEN
|
|
|
|
b startup
|
|
|
|
# The 64-bit main entry point and for restart after relocation.
|
|
|
|
.org 0x500
|
|
.globl startup
|
|
startup:
|
|
# Disable intrrupts globally
|
|
li.w $t0, (0x1 << 2)
|
|
csrxchg $zero, $t0, LOONGARCH_CSR_CRMD
|
|
|
|
# Some of the startup actions are not thread safe. Use a mutex
|
|
# to protect this section of code.
|
|
la.pcrel $t0, startup_mutex
|
|
li.w $t1, 0x1
|
|
Locked:
|
|
amswap_db.w $t2, $t1, $t0
|
|
bnez $t2, Locked
|
|
|
|
Critical:
|
|
# Set cores stack
|
|
csrrd $t0, LOONGARCH_CSR_CPUID
|
|
li.d $t1, AP_STACK_SIZE
|
|
mul.d $t0, $t0, $t1
|
|
li.d $t1, BSP_STACK_SIZE - LOCALS_SIZE
|
|
add.d $t0, $t0, $t1
|
|
la.pcrel $sp, _stacks
|
|
add.d $sp, $sp, $t0
|
|
|
|
SetException:
|
|
# Only enable the IPI
|
|
li.w $t0, 0x1FFF
|
|
csrxchg $zero, $t0, LOONGARCH_CSR_ECFG
|
|
li.w $t0, (0x1 << 12)
|
|
csrxchg $t0, $t0, LOONGARCH_CSR_ECFG
|
|
|
|
# Set exception base
|
|
csrrd $t0, LOONGARCH_CSR_ECFG
|
|
bstrins.w $t0, $zero, 18, 16
|
|
csrwr $t0, LOONGARCH_CSR_ECFG
|
|
la.pcrel $t0, exception_entry
|
|
csrwr $t0, LOONGARCH_CSR_EBASE # Exception and interrupts
|
|
la.pcrel $t0, exception_entry
|
|
csrwr $t0, LOONGARCH_CSR_TLBREBASE # TLB refill
|
|
|
|
# Enable intrrupts globally
|
|
li.w $t0, (0x1 << 2)
|
|
csrxchg $t0, $t0, LOONGARCH_CSR_CRMD
|
|
|
|
bl reloc
|
|
|
|
# Release the startup mutex.
|
|
la.pcrel $t0, startup_mutex
|
|
li.w $t1, 0x0
|
|
amswap_db.w $zero, $t1, $t0
|
|
|
|
# Run the application.
|
|
bl main
|
|
|
|
.previous
|
|
|
|
# Variables.
|
|
|
|
.data
|
|
.align 4
|
|
|
|
.globl ap_startup_addr
|
|
ap_startup_addr:
|
|
.quad 0 # filled in at run time
|
|
|
|
.globl boot_params_addr
|
|
boot_params_addr:
|
|
.quad 0
|
|
|
|
startup_mutex:
|
|
.long 0
|
|
|
|
first_boot:
|
|
.long 1
|
|
|
|
.previous
|
|
|
|
# Startup stack.
|
|
|
|
.bss
|
|
.align 16
|
|
|
|
startup_stack_base:
|
|
. = . + 64
|
|
startup_stack_top:
|
|
|
|
.previous
|
|
|
|
# Main stack area.
|
|
|
|
.section ".stacks", "aw", @nobits
|
|
.align 16
|
|
|
|
. = . + STACKS_SIZE
|
|
|
|
.previous
|