1/* 2 * relocate_kernel.S - put the kernel image in place to boot 3 * Copyright (C) 2002-2005 Eric Biederman <ebiederm@xmission.com> 4 * 5 * This source code is licensed under the GNU General Public License, 6 * Version 2. See the file COPYING for more details. 7 */ 8 9#include <linux/linkage.h> 10#include <asm/page_types.h> 11#include <asm/kexec.h> 12#include <asm/processor-flags.h> 13#include <asm/pgtable_types.h> 14 15/* 16 * Must be relocatable PIC code callable as a C function 17 */ 18 19#define PTR(x) (x << 3) 20#define PAGE_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY) 21 22/* 23 * control_page + KEXEC_CONTROL_CODE_MAX_SIZE 24 * ~ control_page + PAGE_SIZE are used as data storage and stack for 25 * jumping back 26 */ 27#define DATA(offset) (KEXEC_CONTROL_CODE_MAX_SIZE+(offset)) 28 29/* Minimal CPU state */ 30#define RSP DATA(0x0) 31#define CR0 DATA(0x8) 32#define CR3 DATA(0x10) 33#define CR4 DATA(0x18) 34 35/* other data */ 36#define CP_PA_TABLE_PAGE DATA(0x20) 37#define CP_PA_SWAP_PAGE DATA(0x28) 38#define CP_PA_BACKUP_PAGES_MAP DATA(0x30) 39 40 .text 41 .align PAGE_SIZE 42 .code64 43 .globl relocate_kernel 44relocate_kernel: 45 /* 46 * %rdi indirection_page 47 * %rsi page_list 48 * %rdx start address 49 * %rcx preserve_context 50 */ 51 52 /* Save the CPU context, used for jumping back */ 53 pushq %rbx 54 pushq %rbp 55 pushq %r12 56 pushq %r13 57 pushq %r14 58 pushq %r15 59 pushf 60 61 movq PTR(VA_CONTROL_PAGE)(%rsi), %r11 62 movq %rsp, RSP(%r11) 63 movq %cr0, %rax 64 movq %rax, CR0(%r11) 65 movq %cr3, %rax 66 movq %rax, CR3(%r11) 67 movq %cr4, %rax 68 movq %rax, CR4(%r11) 69 70 /* zero out flags, and disable interrupts */ 71 pushq $0 72 popfq 73 74 /* 75 * get physical address of control page now 76 * this is impossible after page table switch 77 */ 78 movq PTR(PA_CONTROL_PAGE)(%rsi), %r8 79 80 /* get physical address of page table now too */ 81 movq PTR(PA_TABLE_PAGE)(%rsi), %r9 82 83 /* get physical address of swap page now */ 84 movq PTR(PA_SWAP_PAGE)(%rsi), %r10 85 86 /* save some information for jumping back */ 87 movq %r9, CP_PA_TABLE_PAGE(%r11) 88 movq %r10, CP_PA_SWAP_PAGE(%r11) 89 movq %rdi, CP_PA_BACKUP_PAGES_MAP(%r11) 90 91 /* Switch to the identity mapped page tables */ 92 movq %r9, %cr3 93 94 /* setup a new stack at the end of the physical control page */ 95 lea PAGE_SIZE(%r8), %rsp 96 97 /* jump to identity mapped page */ 98 addq $(identity_mapped - relocate_kernel), %r8 99 pushq %r8 100 ret 101 102identity_mapped: 103 /* set return address to 0 if not preserving context */ 104 pushq $0 105 /* store the start address on the stack */ 106 pushq %rdx 107 108 /* 109 * Set cr0 to a known state: 110 * - Paging enabled 111 * - Alignment check disabled 112 * - Write protect disabled 113 * - No task switch 114 * - Don't do FP software emulation. 115 * - Proctected mode enabled 116 */ 117 movq %cr0, %rax 118 andq $~(X86_CR0_AM | X86_CR0_WP | X86_CR0_TS | X86_CR0_EM), %rax 119 orl $(X86_CR0_PG | X86_CR0_PE), %eax 120 movq %rax, %cr0 121 122 /* 123 * Set cr4 to a known state: 124 * - physical address extension enabled 125 */ 126 movq $X86_CR4_PAE, %rax 127 movq %rax, %cr4 128 129 jmp 1f 1301: 131 132 /* Flush the TLB (needed?) */ 133 movq %r9, %cr3 134 135 movq %rcx, %r11 136 call swap_pages 137 138 /* 139 * To be certain of avoiding problems with self-modifying code 140 * I need to execute a serializing instruction here. 141 * So I flush the TLB by reloading %cr3 here, it's handy, 142 * and not processor dependent. 143 */ 144 movq %cr3, %rax 145 movq %rax, %cr3 146 147 /* 148 * set all of the registers to known values 149 * leave %rsp alone 150 */ 151 152 testq %r11, %r11 153 jnz 1f 154 xorq %rax, %rax 155 xorq %rbx, %rbx 156 xorq %rcx, %rcx 157 xorq %rdx, %rdx 158 xorq %rsi, %rsi 159 xorq %rdi, %rdi 160 xorq %rbp, %rbp 161 xorq %r8, %r8 162 xorq %r9, %r9 163 xorq %r10, %r9 164 xorq %r11, %r11 165 xorq %r12, %r12 166 xorq %r13, %r13 167 xorq %r14, %r14 168 xorq %r15, %r15 169 170 ret 171 1721: 173 popq %rdx 174 leaq PAGE_SIZE(%r10), %rsp 175 call *%rdx 176 177 /* get the re-entry point of the peer system */ 178 movq 0(%rsp), %rbp 179 call 1f 1801: 181 popq %r8 182 subq $(1b - relocate_kernel), %r8 183 movq CP_PA_SWAP_PAGE(%r8), %r10 184 movq CP_PA_BACKUP_PAGES_MAP(%r8), %rdi 185 movq CP_PA_TABLE_PAGE(%r8), %rax 186 movq %rax, %cr3 187 lea PAGE_SIZE(%r8), %rsp 188 call swap_pages 189 movq $virtual_mapped, %rax 190 pushq %rax 191 ret 192 193virtual_mapped: 194 movq RSP(%r8), %rsp 195 movq CR4(%r8), %rax 196 movq %rax, %cr4 197 movq CR3(%r8), %rax 198 movq CR0(%r8), %r8 199 movq %rax, %cr3 200 movq %r8, %cr0 201 movq %rbp, %rax 202 203 popf 204 popq %r15 205 popq %r14 206 popq %r13 207 popq %r12 208 popq %rbp 209 popq %rbx 210 ret 211 212 /* Do the copies */ 213swap_pages: 214 movq %rdi, %rcx /* Put the page_list in %rcx */ 215 xorq %rdi, %rdi 216 xorq %rsi, %rsi 217 jmp 1f 218 2190: /* top, read another word for the indirection page */ 220 221 movq (%rbx), %rcx 222 addq $8, %rbx 2231: 224 testq $0x1, %rcx /* is it a destination page? */ 225 jz 2f 226 movq %rcx, %rdi 227 andq $0xfffffffffffff000, %rdi 228 jmp 0b 2292: 230 testq $0x2, %rcx /* is it an indirection page? */ 231 jz 2f 232 movq %rcx, %rbx 233 andq $0xfffffffffffff000, %rbx 234 jmp 0b 2352: 236 testq $0x4, %rcx /* is it the done indicator? */ 237 jz 2f 238 jmp 3f 2392: 240 testq $0x8, %rcx /* is it the source indicator? */ 241 jz 0b /* Ignore it otherwise */ 242 movq %rcx, %rsi /* For ever source page do a copy */ 243 andq $0xfffffffffffff000, %rsi 244 245 movq %rdi, %rdx 246 movq %rsi, %rax 247 248 movq %r10, %rdi 249 movq $512, %rcx 250 rep ; movsq 251 252 movq %rax, %rdi 253 movq %rdx, %rsi 254 movq $512, %rcx 255 rep ; movsq 256 257 movq %rdx, %rdi 258 movq %r10, %rsi 259 movq $512, %rcx 260 rep ; movsq 261 262 lea PAGE_SIZE(%rax), %rsi 263 jmp 0b 2643: 265 ret 266 267 .globl kexec_control_code_size 268.set kexec_control_code_size, . - relocate_kernel 269