mirror of
https://gitee.com/open-loongarch/linux-5.10.git
synced 2026-10-25 20:57:11 +08:00
213 lines
4.5 KiB
ArmAsm
213 lines
4.5 KiB
ArmAsm
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* relocate_kernel.S for kexec
|
|
* Created by <nschichan@corp.free.fr> on Thu Oct 12 17:49:57 2006
|
|
* Copyright (C) 2020 Loongson Technology Co., Ltd.
|
|
*
|
|
* This source code is licensed under the GNU General Public License,
|
|
* Version 2. See the file COPYING for more details.
|
|
*/
|
|
|
|
#include <asm/asm.h>
|
|
#include <asm/asmmacro.h>
|
|
#include <asm/regdef.h>
|
|
#include <asm/loongarchregs.h>
|
|
#include <asm/stackframe.h>
|
|
#include <asm/addrspace.h>
|
|
|
|
SYM_CODE_START(relocate_new_kernel)
|
|
la t0, arg0
|
|
PTR_L a0, t0, 0
|
|
la t0, arg1
|
|
PTR_L a1, t0, 0
|
|
la t0, arg2
|
|
PTR_L a2, t0, 0
|
|
la t0, arg3
|
|
PTR_L a3, t0, 0
|
|
|
|
la t0, kexec_indirection_page
|
|
PTR_L s0, t0, 0
|
|
la t0, kexec_start_address
|
|
PTR_L s1, t0, 0
|
|
|
|
process_entry:
|
|
PTR_L s2, s0, 0
|
|
PTR_ADDIU s0, s0, SZREG
|
|
|
|
/*
|
|
* In case of a kdump/crash kernel, the indirection page is not
|
|
* populated as the kernel is directly copied to a reserved location
|
|
*/
|
|
beqz s2, done
|
|
|
|
/* destination page */
|
|
andi s3, s2, 0x1
|
|
beq s3, zero, 1f
|
|
li.w t0, ~0x1
|
|
and s4, s2, t0 /* store destination addr in s4 */
|
|
b process_entry
|
|
|
|
1:
|
|
/* indirection page, update s0 */
|
|
andi s3, s2, 0x2
|
|
beq s3, zero, 1f
|
|
li.w t0, ~0x2
|
|
and s0, s2, t0
|
|
b process_entry
|
|
|
|
1:
|
|
/* done page */
|
|
andi s3, s2, 0x4
|
|
beq s3, zero, 1f
|
|
b done
|
|
1:
|
|
/* source page */
|
|
andi s3, s2, 0x8
|
|
beq s3, zero, process_entry
|
|
li.w t0, ~0x8
|
|
and s2, s2, t0
|
|
li.w s6, (1 << _PAGE_SHIFT) / SZREG
|
|
|
|
copy_word:
|
|
/* copy page word by word */
|
|
REG_L s5, s2, 0
|
|
REG_S s5, s4, 0
|
|
PTR_ADDIU s4, s4, SZREG
|
|
PTR_ADDIU s2, s2, SZREG
|
|
LONG_ADDIU s6, s6, -1
|
|
beq s6, zero, process_entry
|
|
b copy_word
|
|
b process_entry
|
|
|
|
done:
|
|
#ifdef CONFIG_SMP
|
|
/*
|
|
* kexec_flag reset is signal to other CPUs what kernel
|
|
* was moved to it's location. Note - we need relocated
|
|
* address of kexec_flag.
|
|
*/
|
|
bl 1f
|
|
1: move t1,ra;
|
|
la t2,1b
|
|
la t0,kexec_flag
|
|
PTR_SUBU t0,t0,t2;
|
|
PTR_ADDU t0,t1,t0;
|
|
LONG_S zero,t0,0
|
|
#endif
|
|
|
|
dbar 0
|
|
/* jump to kexec_start_address */
|
|
jr s1
|
|
SYM_CODE_END(relocate_new_kernel)
|
|
|
|
#ifdef CONFIG_SMP
|
|
/*
|
|
* Other CPUs should wait until code is relocated and
|
|
* then start at entry (?) point.
|
|
*/
|
|
SYM_CODE_START(kexec_smp_wait)
|
|
la t0, s_arg0
|
|
PTR_L a0, t0, 0
|
|
la t0, s_arg1
|
|
PTR_L a1, t0, 0
|
|
la t0, s_arg2
|
|
PTR_L a2, t0, 0
|
|
la t0, s_arg3
|
|
PTR_L a3, t0, 0
|
|
la t0, kexec_start_address
|
|
PTR_L s1, t0, 0
|
|
|
|
/*
|
|
* Non-relocated address works for args and kexec_start_address (old
|
|
* kernel is not overwritten). But we need relocated address of
|
|
* kexec_flag.
|
|
*/
|
|
bl 1f
|
|
1: move t1,ra;
|
|
la t2,1b
|
|
la t0,kexec_flag
|
|
PTR_SUBU t0,t0,t2;
|
|
PTR_ADDU t0,t1,t0;
|
|
|
|
1: LONG_L s0, t0, 0
|
|
bne s0, zero,1b
|
|
|
|
dbar 0
|
|
|
|
#ifdef CONFIG_CPU_LOONGSON64
|
|
/*
|
|
* s1:initfn
|
|
* a0:base t1:cpuid t2:node t3:core t4:count
|
|
*/
|
|
csrrd t1, LOONGARCH_CSR_CPUID
|
|
andi t1, t1, CSR_CPUID_COREID
|
|
andi t3, t1, 0x3
|
|
slli.w t3, t3, 8 /* get core id */
|
|
or a0, a0, t3
|
|
andi t2, t1, 0x3c
|
|
slli.d t2, t2, 42 /* get node id */
|
|
or a0, a0, t2
|
|
1: li.w t4, 0x100 /* wait for init loop */
|
|
2: addi.w t4, t4, -1 /* limit mailbox access */
|
|
bnez t4, 2b
|
|
ld.w s1, a0, 0x20 /* check PC as an indicator */
|
|
beqz s1, 1b
|
|
ld.d s1, a0, 0x20 /* get PC via mailbox */
|
|
ld.d sp, a0, 0x28 /* get SP via mailbox */
|
|
ld.d tp, a0, 0x30 /* get TP via mailbox */
|
|
ld.d a1, a0, 0x38
|
|
#endif
|
|
|
|
jr s1 /* jump to initial PC */
|
|
SYM_CODE_END(kexec_smp_wait)
|
|
#endif
|
|
|
|
#ifdef __loongarch64
|
|
/* all PTR's must be aligned to 8 byte in 64-bit mode */
|
|
.align 3
|
|
#endif
|
|
|
|
/*
|
|
* All parameters to new kernel are passed in registers a0-a3.
|
|
* kexec_args[0..3] are used to prepare register values.
|
|
*/
|
|
SYM_DATA_START(kexec_args)
|
|
arg0: PTR 0x0
|
|
arg1: PTR 0x0
|
|
arg2: PTR 0x0
|
|
arg3: PTR 0x0
|
|
SYM_DATA_END(kexec_args)
|
|
|
|
#ifdef CONFIG_SMP
|
|
/*
|
|
* Secondary CPUs may have different kernel parameters in
|
|
* their registers a0-a3. secondary_kexec_args[0..3] are used
|
|
* to prepare register values.
|
|
*/
|
|
SYM_DATA_START(secondary_kexec_args)
|
|
s_arg0: PTR 0x0
|
|
s_arg1: PTR 0x0
|
|
s_arg2: PTR 0x0
|
|
s_arg3: PTR 0x0
|
|
SYM_DATA_END(secondary_kexec_args)
|
|
|
|
SYM_DATA_START(kexec_flag)
|
|
LONG 0x1
|
|
SYM_DATA_END(kexec_flag)
|
|
|
|
#endif
|
|
|
|
SYM_DATA_START(kexec_start_address)
|
|
PTR 0x0
|
|
SYM_DATA_END(kexec_start_address)
|
|
|
|
SYM_DATA_START(kexec_indirection_page)
|
|
PTR 0
|
|
SYM_DATA_END(kexec_indirection_page)
|
|
|
|
relocate_new_kernel_end:
|
|
|
|
SYM_DATA_START(relocate_new_kernel_size)
|
|
PTR relocate_new_kernel_end - relocate_new_kernel
|
|
SYM_DATA_END(relocate_new_kernel_size)
|