1/* SPDX-License-Identifier: GPL-2.0-only */ 2/* 3 * relocate_kernel.S - put the kernel image in place to boot 4 * Copyright (C) 2002-2004 Eric Biederman <ebiederm@xmission.com> 5 */ 6 7#include <linux/linkage.h> 8#include <asm/page_types.h> 9#include <asm/kexec.h> 10#include <asm/processor-flags.h> 11 12/* 13 * Must be relocatable PIC code callable as a C function 14 */ 15 16#define PTR(x) (x << 2) 17 18/* 19 * control_page + KEXEC_CONTROL_CODE_MAX_SIZE 20 * ~ control_page + PAGE_SIZE are used as data storage and stack for 21 * jumping back 22 */ 23#define DATA(offset) (KEXEC_CONTROL_CODE_MAX_SIZE+(offset)) 24 25/* Minimal CPU state */ 26#define ESP DATA(0x0) 27#define CR0 DATA(0x4) 28#define CR3 DATA(0x8) 29#define CR4 DATA(0xc) 30 31/* other data */ 32#define CP_VA_CONTROL_PAGE DATA(0x10) 33#define CP_PA_PGD DATA(0x14) 34#define CP_PA_SWAP_PAGE DATA(0x18) 35#define CP_PA_BACKUP_PAGES_MAP DATA(0x1c) 36 37 .text 38 .globl relocate_kernel 39relocate_kernel: 40 /* Save the CPU context, used for jumping back */ 41 42 pushl %ebx 43 pushl %esi 44 pushl %edi 45 pushl %ebp 46 pushf 47 48 movl 20+8(%esp), %ebp /* list of pages */ 49 movl PTR(VA_CONTROL_PAGE)(%ebp), %edi 50 movl %esp, ESP(%edi) 51 movl %cr0, %eax 52 movl %eax, CR0(%edi) 53 movl %cr3, %eax 54 movl %eax, CR3(%edi) 55 movl %cr4, %eax 56 movl %eax, CR4(%edi) 57 58 /* read the arguments and say goodbye to the stack */ 59 movl 20+4(%esp), %ebx /* page_list */ 60 movl 20+8(%esp), %ebp /* list of pages */ 61 movl 20+12(%esp), %edx /* start address */ 62 movl 20+16(%esp), %ecx /* cpu_has_pae */ 63 movl 20+20(%esp), %esi /* preserve_context */ 64 65 /* zero out flags, and disable interrupts */ 66 pushl $0 67 popfl 68 69 /* save some information for jumping back */ 70 movl PTR(VA_CONTROL_PAGE)(%ebp), %edi 71 movl %edi, CP_VA_CONTROL_PAGE(%edi) 72 movl PTR(PA_PGD)(%ebp), %eax 73 movl %eax, CP_PA_PGD(%edi) 74 movl PTR(PA_SWAP_PAGE)(%ebp), %eax 75 movl %eax, CP_PA_SWAP_PAGE(%edi) 76 movl %ebx, CP_PA_BACKUP_PAGES_MAP(%edi) 77 78 /* 79 * get physical address of control page now 80 * this is impossible after page table switch 81 */ 82 movl PTR(PA_CONTROL_PAGE)(%ebp), %edi 83 84 /* switch to new set of page tables */ 85 movl PTR(PA_PGD)(%ebp), %eax 86 movl %eax, %cr3 87 88 /* setup a new stack at the end of the physical control page */ 89 lea PAGE_SIZE(%edi), %esp 90 91 /* jump to identity mapped page */ 92 movl %edi, %eax 93 addl $(identity_mapped - relocate_kernel), %eax 94 pushl %eax 95 ret 96 97identity_mapped: 98 /* set return address to 0 if not preserving context */ 99 pushl $0 100 /* store the start address on the stack */ 101 pushl %edx 102 103 /* 104 * Set cr0 to a known state: 105 * - Paging disabled 106 * - Alignment check disabled 107 * - Write protect disabled 108 * - No task switch 109 * - Don't do FP software emulation. 110 * - Proctected mode enabled 111 */ 112 movl %cr0, %eax 113 andl $~(X86_CR0_PG | X86_CR0_AM | X86_CR0_WP | X86_CR0_TS | X86_CR0_EM), %eax 114 orl $(X86_CR0_PE), %eax 115 movl %eax, %cr0 116 117 /* clear cr4 if applicable */ 118 testl %ecx, %ecx 119 jz 1f 120 /* 121 * Set cr4 to a known state: 122 * Setting everything to zero seems safe. 123 */ 124 xorl %eax, %eax 125 movl %eax, %cr4 126 127 jmp 1f 1281: 129 130 /* Flush the TLB (needed?) */ 131 xorl %eax, %eax 132 movl %eax, %cr3 133 134 movl CP_PA_SWAP_PAGE(%edi), %eax 135 pushl %eax 136 pushl %ebx 137 call swap_pages 138 addl $8, %esp 139 140 /* 141 * To be certain of avoiding problems with self-modifying code 142 * I need to execute a serializing instruction here. 143 * So I flush the TLB, it's handy, and not processor dependent. 144 */ 145 xorl %eax, %eax 146 movl %eax, %cr3 147 148 /* 149 * set all of the registers to known values 150 * leave %esp alone 151 */ 152 153 testl %esi, %esi 154 jnz 1f 155 xorl %edi, %edi 156 xorl %eax, %eax 157 xorl %ebx, %ebx 158 xorl %ecx, %ecx 159 xorl %edx, %edx 160 xorl %esi, %esi 161 xorl %ebp, %ebp 162 ret 1631: 164 popl %edx 165 movl CP_PA_SWAP_PAGE(%edi), %esp 166 addl $PAGE_SIZE, %esp 1672: 168 call *%edx 169 170 /* get the re-entry point of the peer system */ 171 movl 0(%esp), %ebp 172 call 1f 1731: 174 popl %ebx 175 subl $(1b - relocate_kernel), %ebx 176 movl CP_VA_CONTROL_PAGE(%ebx), %edi 177 lea PAGE_SIZE(%ebx), %esp 178 movl CP_PA_SWAP_PAGE(%ebx), %eax 179 movl CP_PA_BACKUP_PAGES_MAP(%ebx), %edx 180 pushl %eax 181 pushl %edx 182 call swap_pages 183 addl $8, %esp 184 movl CP_PA_PGD(%ebx), %eax 185 movl %eax, %cr3 186 movl %cr0, %eax 187 orl $X86_CR0_PG, %eax 188 movl %eax, %cr0 189 lea PAGE_SIZE(%edi), %esp 190 movl %edi, %eax 191 addl $(virtual_mapped - relocate_kernel), %eax 192 pushl %eax 193 ret 194 195virtual_mapped: 196 movl CR4(%edi), %eax 197 movl %eax, %cr4 198 movl CR3(%edi), %eax 199 movl %eax, %cr3 200 movl CR0(%edi), %eax 201 movl %eax, %cr0 202 movl ESP(%edi), %esp 203 movl %ebp, %eax 204 205 popf 206 popl %ebp 207 popl %edi 208 popl %esi 209 popl %ebx 210 ret 211 212 /* Do the copies */ 213swap_pages: 214 movl 8(%esp), %edx 215 movl 4(%esp), %ecx 216 pushl %ebp 217 pushl %ebx 218 pushl %edi 219 pushl %esi 220 movl %ecx, %ebx 221 jmp 1f 222 2230: /* top, read another word from the indirection page */ 224 movl (%ebx), %ecx 225 addl $4, %ebx 2261: 227 testb $0x1, %cl /* is it a destination page */ 228 jz 2f 229 movl %ecx, %edi 230 andl $0xfffff000, %edi 231 jmp 0b 2322: 233 testb $0x2, %cl /* is it an indirection page */ 234 jz 2f 235 movl %ecx, %ebx 236 andl $0xfffff000, %ebx 237 jmp 0b 2382: 239 testb $0x4, %cl /* is it the done indicator */ 240 jz 2f 241 jmp 3f 2422: 243 testb $0x8, %cl /* is it the source indicator */ 244 jz 0b /* Ignore it otherwise */ 245 movl %ecx, %esi /* For every source page do a copy */ 246 andl $0xfffff000, %esi 247 248 movl %edi, %eax 249 movl %esi, %ebp 250 251 movl %edx, %edi 252 movl $1024, %ecx 253 rep ; movsl 254 255 movl %ebp, %edi 256 movl %eax, %esi 257 movl $1024, %ecx 258 rep ; movsl 259 260 movl %eax, %edi 261 movl %edx, %esi 262 movl $1024, %ecx 263 rep ; movsl 264 265 lea PAGE_SIZE(%ebp), %esi 266 jmp 0b 2673: 268 popl %esi 269 popl %edi 270 popl %ebx 271 popl %ebp 272 ret 273 274 .globl kexec_control_code_size 275.set kexec_control_code_size, . - relocate_kernel 276