1 | /* SPDX-License-Identifier: GPL-2.0 */ |
2 | /* |
3 | * relocate_kernel.S for kexec |
4 | * |
5 | * Copyright (C) 2022 Loongson Technology Corporation Limited |
6 | */ |
7 | |
8 | #include <linux/kexec.h> |
9 | |
10 | #include <asm/asm.h> |
11 | #include <asm/asmmacro.h> |
12 | #include <asm/regdef.h> |
13 | #include <asm/loongarch.h> |
14 | #include <asm/stackframe.h> |
15 | #include <asm/addrspace.h> |
16 | |
17 | SYM_CODE_START(relocate_new_kernel) |
18 | UNWIND_HINT_UNDEFINED |
19 | /* |
20 | * a0: EFI boot flag for the new kernel |
21 | * a1: Command line pointer for the new kernel |
22 | * a2: System table pointer for the new kernel |
23 | * a3: Start address to jump to after relocation |
24 | * a4: Pointer to the current indirection page entry |
25 | */ |
26 | move s0, a4 |
27 | |
28 | /* |
29 | * In case of a kdump/crash kernel, the indirection page is not |
30 | * populated as the kernel is directly copied to a reserved location |
31 | */ |
32 | beqz s0, done |
33 | |
34 | process_entry: |
35 | PTR_L s1, s0, 0 |
36 | PTR_ADDI s0, s0, SZREG |
37 | |
38 | /* destination page */ |
39 | andi s2, s1, IND_DESTINATION |
40 | beqz s2, 1f |
41 | li.w t0, ~0x1 |
42 | and s3, s1, t0 /* store destination addr in s3 */ |
43 | b process_entry |
44 | |
45 | 1: |
46 | /* indirection page, update s0 */ |
47 | andi s2, s1, IND_INDIRECTION |
48 | beqz s2, 1f |
49 | li.w t0, ~0x2 |
50 | and s0, s1, t0 |
51 | b process_entry |
52 | |
53 | 1: |
54 | /* done page */ |
55 | andi s2, s1, IND_DONE |
56 | beqz s2, 1f |
57 | b done |
58 | |
59 | 1: |
60 | /* source page */ |
61 | andi s2, s1, IND_SOURCE |
62 | beqz s2, process_entry |
63 | li.w t0, ~0x8 |
64 | and s1, s1, t0 |
65 | li.w s5, (1 << _PAGE_SHIFT) / SZREG |
66 | |
67 | copy_word: |
68 | /* copy page word by word */ |
69 | REG_L s4, s1, 0 |
70 | REG_S s4, s3, 0 |
71 | PTR_ADDI s3, s3, SZREG |
72 | PTR_ADDI s1, s1, SZREG |
73 | LONG_ADDI s5, s5, -1 |
74 | beqz s5, process_entry |
75 | b copy_word |
76 | |
77 | done: |
78 | ibar 0 |
79 | dbar 0 |
80 | |
81 | /* |
82 | * Jump to the new kernel, |
83 | * make sure the values of a0, a1, a2 and a3 are not changed. |
84 | */ |
85 | jr a3 |
86 | SYM_CODE_END(relocate_new_kernel) |
87 | |
88 | #ifdef CONFIG_SMP |
89 | /* |
90 | * Other CPUs should wait until code is relocated and |
91 | * then start at the entry point from LOONGARCH_IOCSR_MBUF0. |
92 | */ |
93 | SYM_CODE_START(kexec_smp_wait) |
94 | UNWIND_HINT_UNDEFINED |
95 | 1: li.w t0, 0x100 /* wait for init loop */ |
96 | 2: addi.w t0, t0, -1 /* limit mailbox access */ |
97 | bnez t0, 2b |
98 | li.w t1, LOONGARCH_IOCSR_MBUF0 |
99 | iocsrrd.w s0, t1 /* check PC as an indicator */ |
100 | beqz s0, 1b |
101 | iocsrrd.d s0, t1 /* get PC via mailbox */ |
102 | |
103 | li.d t0, CACHE_BASE |
104 | or s0, s0, t0 /* s0 = TO_CACHE(s0) */ |
105 | jr s0 /* jump to initial PC */ |
106 | SYM_CODE_END(kexec_smp_wait) |
107 | #endif |
108 | |
109 | relocate_new_kernel_end: |
110 | |
111 | .section ".data" |
112 | SYM_DATA(relocate_new_kernel_size, .long relocate_new_kernel_end - relocate_new_kernel) |
113 | |