| 1 | /* SPDX-License-Identifier: GPL-2.0-only */
|
| 2 | /*
|
| 3 | * relocate_kernel.S for kexec
|
| 4 | * Created by <nschichan@corp.free.fr> on Thu Oct 12 17:49:57 2006
|
| 5 | */
|
| 6 |
|
| 7 | |
| 8 | |
| 9 | |
| 10 | |
| 11 | |
| 12 | |
| 13 |
|
| 14 | |
| 15 |
|
| 16 | LEAF(relocate_new_kernel)
|
| 17 | PTR_L a0, arg0
|
| 18 | PTR_L a1, arg1
|
| 19 | PTR_L a2, arg2
|
| 20 | PTR_L a3, arg3
|
| 21 |
|
| 22 | PTR_L s0, kexec_indirection_page
|
| 23 | PTR_L s1, kexec_start_address
|
| 24 |
|
| 25 | process_entry:
|
| 26 | PTR_L s2, (s0)
|
| 27 | PTR_ADDIU s0, s0, SZREG
|
| 28 |
|
| 29 | /*
|
| 30 | * In case of a kdump/crash kernel, the indirection page is not
|
| 31 | * populated as the kernel is directly copied to a reserved location
|
| 32 | */
|
| 33 | beqz s2, done
|
| 34 |
|
| 35 | /* destination page */
|
| 36 | and s3, s2, 0x1
|
| 37 | beq s3, zero, 1f
|
| 38 | and s4, s2, ~0x1 /* store destination addr in s4 */
|
| 39 | b process_entry
|
| 40 |
|
| 41 | 1:
|
| 42 | /* indirection page, update s0 */
|
| 43 | and s3, s2, 0x2
|
| 44 | beq s3, zero, 1f
|
| 45 | and s0, s2, ~0x2
|
| 46 | b process_entry
|
| 47 |
|
| 48 | 1:
|
| 49 | /* done page */
|
| 50 | and s3, s2, 0x4
|
| 51 | beq s3, zero, 1f
|
| 52 | b done
|
| 53 | 1:
|
| 54 | /* source page */
|
| 55 | and s3, s2, 0x8
|
| 56 | beq s3, zero, process_entry
|
| 57 | and s2, s2, ~0x8
|
| 58 | li s6, (1 << _PAGE_SHIFT) / SZREG
|
| 59 |
|
| 60 | copy_word:
|
| 61 | /* copy page word by word */
|
| 62 | REG_L s5, (s2)
|
| 63 | REG_S s5, (s4)
|
| 64 | PTR_ADDIU s4, s4, SZREG
|
| 65 | PTR_ADDIU s2, s2, SZREG
|
| 66 | LONG_ADDIU s6, s6, -1
|
| 67 | beq s6, zero, process_entry
|
| 68 | b copy_word
|
| 69 |
|
| 70 | done:
|
| 71 | |
| 72 | /* kexec_flag reset is signal to other CPUs what kernel
|
| 73 | was moved to its location. Note - we need relocated address
|
| 74 | of kexec_flag. */
|
| 75 |
|
| 76 | bal 1f
|
| 77 | 1: move t1,ra;
|
| 78 | PTR_LA t2,1b
|
| 79 | PTR_LA t0,kexec_flag
|
| 80 | PTR_SUB t0,t0,t2;
|
| 81 | PTR_ADD t0,t1,t0;
|
| 82 | LONG_S zero,(t0)
|
| 83 | |
| 84 |
|
| 85 | |
| 86 | /* We need to flush I-cache before jumping to new kernel.
|
| 87 | * Unfortunately, this code is cpu-specific.
|
| 88 | */
|
| 89 | .set push
|
| 90 | .set noreorder
|
| 91 | syncw
|
| 92 | syncw
|
| 93 | synci 0($0)
|
| 94 | .set pop
|
| 95 | |
| 96 | sync
|
| 97 | |
| 98 | /* jump to kexec_start_address */
|
| 99 | j s1
|
| 100 | END(relocate_new_kernel)
|
| 101 |
|
| 102 | |
| 103 | /*
|
| 104 | * Other CPUs should wait until code is relocated and
|
| 105 | * then start at entry (?) point.
|
| 106 | */
|
| 107 | LEAF(kexec_smp_wait)
|
| 108 | PTR_L a0, s_arg0
|
| 109 | PTR_L a1, s_arg1
|
| 110 | PTR_L a2, s_arg2
|
| 111 | PTR_L a3, s_arg3
|
| 112 | PTR_L s1, kexec_start_address
|
| 113 |
|
| 114 | /* Non-relocated address works for args and kexec_start_address ( old
|
| 115 | * kernel is not overwritten). But we need relocated address of
|
| 116 | * kexec_flag.
|
| 117 | */
|
| 118 |
|
| 119 | bal 1f
|
| 120 | 1: move t1,ra;
|
| 121 | PTR_LA t2,1b
|
| 122 | PTR_LA t0,kexec_flag
|
| 123 | PTR_SUB t0,t0,t2;
|
| 124 | PTR_ADD t0,t1,t0;
|
| 125 |
|
| 126 | 1: LONG_L s0, (t0)
|
| 127 | bne s0, zero,1b
|
| 128 |
|
| 129 | |
| 130 | kexec_smp_wait_final
|
| 131 | |
| 132 | sync
|
| 133 | |
| 134 | j s1
|
| 135 | END(kexec_smp_wait)
|
| 136 | |
| 137 |
|
| 138 | |
| 139 | /* all PTR's must be aligned to 8 byte in 64-bit mode */
|
| 140 | .align 3
|
| 141 | #endif
|
| 142 |
|
| 143 | /* All parameters to new kernel are passed in registers a0-a3.
|
| 144 | * kexec_args[0..3] are used to prepare register values.
|
| 145 | */
|
| 146 |
|
| 147 | EXPORT(kexec_args)
|
| 148 | arg0: PTR_WD 0x0
|
| 149 | arg1: PTR_WD 0x0
|
| 150 | arg2: PTR_WD 0x0
|
| 151 | arg3: PTR_WD 0x0
|
| 152 | .size kexec_args,PTRSIZE*4
|
| 153 |
|
| 154 | #ifdef CONFIG_SMP
|
| 155 | /*
|
| 156 | * Secondary CPUs may have different kernel parameters in
|
| 157 | * their registers a0-a3. secondary_kexec_args[0..3] are used
|
| 158 | * to prepare register values.
|
| 159 | */
|
| 160 | EXPORT(secondary_kexec_args)
|
| 161 | s_arg0: PTR_WD 0x0
|
| 162 | s_arg1: PTR_WD 0x0
|
| 163 | s_arg2: PTR_WD 0x0
|
| 164 | s_arg3: PTR_WD 0x0
|
| 165 | .size secondary_kexec_args,PTRSIZE*4
|
| 166 | kexec_flag:
|
| 167 | LONG 0x1
|
| 168 |
|
| 169 | #endif
|
| 170 |
|
| 171 | EXPORT(kexec_start_address)
|
| 172 | PTR_WD 0x0
|
| 173 | .size kexec_start_address, PTRSIZE
|
| 174 |
|
| 175 | EXPORT(kexec_indirection_page)
|
| 176 | PTR_WD 0
|
| 177 | .size kexec_indirection_page, PTRSIZE
|
| 178 |
|
| 179 | relocate_new_kernel_end:
|
| 180 |
|
| 181 | EXPORT(relocate_new_kernel_size)
|
| 182 | PTR_WD relocate_new_kernel_end - relocate_new_kernel
|
| 183 | .size relocate_new_kernel_size, PTRSIZE
|