Files
2024-06-17 20:18:01 +08:00

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)