/* * Relocate a kexec_image to its destination and call it. * * Copyright (C) 2013 Citrix Systems R&D Ltd. * * Portions derived from Linux's arch/x86/kernel/relocate_kernel_64.S. * * Copyright (C) 2002-2005 Eric Biederman * * This source code is licensed under the GNU General Public License, * Version 2. See the file COPYING for more details. */ .file __FILE__ #include #include #include #include #include .section .text.kexec, "ax", @progbits .align PAGE_SIZE .code64 ENTRY(kexec_reloc) /* %rdi - code page maddr */ /* %rsi - page table maddr */ /* %rdx - indirection page maddr */ /* %rcx - entry maddr (%rbp) */ /* %r8 - flags */ movq %rcx, %rbp /* Setup stack. */ leaq (reloc_stack - kexec_reloc)(%rdi), %rsp /* Load reloc page table. */ movq %rsi, %cr3 /* Jump to identity mapped code. */ leaq (identity_mapped - kexec_reloc)(%rdi), %rax jmpq *%rax identity_mapped: /* * Set cr0 to a known state: * - Paging enabled * - Alignment check disabled * - Write protect disabled * - No task switch * - Don't do FP software emulation. * - Protected mode enabled */ movq %cr0, %rax andl $~(X86_CR0_AM | X86_CR0_WP | X86_CR0_TS | X86_CR0_EM), %eax orl $(X86_CR0_PG | X86_CR0_PE), %eax movq %rax, %cr0 /* * Set cr4 to a known state: * - physical address extension enabled */ movl $X86_CR4_PAE, %eax movq %rax, %cr4 movq %rdx, %rdi call relocate_pages /* Need to switch to 32-bit mode? */ testq $KEXEC_RELOC_FLAG_COMPAT, %r8 jnz call_32_bit call_64_bit: /* Call the image entry point. This should never return. */ callq *%rbp ud2 call_32_bit: /* Setup IDT. */ lidt compat_mode_idt(%rip) /* Load compat GDT. */ leaq compat_mode_gdt(%rip), %rax movq %rax, (compat_mode_gdt_desc + 2)(%rip) lgdt compat_mode_gdt_desc(%rip) /* Relocate compatibility mode entry point address. */ leal compatibility_mode(%rip), %eax movl %eax, compatibility_mode_far(%rip) /* Enter compatibility mode. */ ljmp *compatibility_mode_far(%rip) relocate_pages: /* %rdi - indirection page maddr */ pushq %rbx cld movq %rdi, %rbx xorl %edi, %edi xorl %esi, %esi next_entry: /* top, read another word for the indirection page */ movq (%rbx), %rcx addq $8, %rbx is_dest: testb $IND_DESTINATION, %cl jz is_ind movq %rcx, %rdi andq $PAGE_MASK, %rdi jmp next_entry is_ind: testb $IND_INDIRECTION, %cl jz is_done movq %rcx, %rbx andq $PAGE_MASK, %rbx jmp next_entry is_done: testb $IND_DONE, %cl jnz done is_source: testb $IND_SOURCE, %cl jz is_zero movq %rcx, %rsi /* For every source page do a copy */ andq $PAGE_MASK, %rsi movl $(PAGE_SIZE / 8), %ecx rep movsq jmp next_entry is_zero: testb $IND_ZERO, %cl jz next_entry movl $(PAGE_SIZE / 8), %ecx /* Zero the destination page. */ xorl %eax, %eax rep stosq jmp next_entry done: popq %rbx ret .code32 compatibility_mode: /* Setup some sane segments. */ movl $0x0008, %eax movl %eax, %ds movl %eax, %es movl %eax, %fs movl %eax, %gs movl %eax, %ss /* Disable paging and therefore leave 64 bit mode. */ movl %cr0, %eax andl $~X86_CR0_PG, %eax movl %eax, %cr0 /* Disable long mode */ movl $MSR_EFER, %ecx rdmsr andl $~EFER_LME, %eax wrmsr /* Clear cr4 to disable PAE. */ xorl %eax, %eax movl %eax, %cr4 /* Call the image entry point. This should never return. */ call *%ebp ud2 .align 4 compatibility_mode_far: .long 0x00000000 /* set in call_32_bit above */ .word 0x0010 compat_mode_gdt_desc: .word (3*8)-1 .quad 0x0000000000000000 /* set in call_32_bit above */ .align 8 compat_mode_gdt: .quad 0x0000000000000000 /* null */ .quad 0x00cf92000000ffff /* 0x0008 ring 0 data */ .quad 0x00cf9a000000ffff /* 0x0010 ring 0 code, compatibility */ compat_mode_idt: .word 0 /* limit */ .long 0 /* base */ /* * 16 words of stack are more than enough. */ .fill 16,8,0 reloc_stack: .globl kexec_reloc_size kexec_reloc_size: .long . - kexec_reloc