kexec jump
[linux-2.6-block.git] / arch / x86 / kernel / relocate_kernel_32.S
index c30fe25d470d8ff63cd0abe40353f6577c12fab4..703310a990239d686516fbcccfc5ede3d05c0cda 100644 (file)
 #define PAGE_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY)
 #define PAE_PGD_ATTR (_PAGE_PRESENT)
 
+/* control_page + PAGE_SIZE/2 ~ control_page + PAGE_SIZE * 3/4 are
+ * used to save some data for jumping back
+ */
+#define DATA(offset)           (PAGE_SIZE/2+(offset))
+
+/* Minimal CPU state */
+#define ESP                    DATA(0x0)
+#define CR0                    DATA(0x4)
+#define CR3                    DATA(0x8)
+#define CR4                    DATA(0xc)
+
+/* other data */
+#define CP_VA_CONTROL_PAGE     DATA(0x10)
+#define CP_PA_PGD              DATA(0x14)
+#define CP_PA_SWAP_PAGE                DATA(0x18)
+#define CP_PA_BACKUP_PAGES_MAP DATA(0x1c)
+
        .text
        .align PAGE_SIZE
        .globl relocate_kernel
 relocate_kernel:
-       movl    8(%esp), %ebp /* list of pages */
+       /* Save the CPU context, used for jumping back */
+
+       pushl   %ebx
+       pushl   %esi
+       pushl   %edi
+       pushl   %ebp
+       pushf
+
+       movl    20+8(%esp), %ebp /* list of pages */
+       movl    PTR(VA_CONTROL_PAGE)(%ebp), %edi
+       movl    %esp, ESP(%edi)
+       movl    %cr0, %eax
+       movl    %eax, CR0(%edi)
+       movl    %cr3, %eax
+       movl    %eax, CR3(%edi)
+       movl    %cr4, %eax
+       movl    %eax, CR4(%edi)
 
 #ifdef CONFIG_X86_PAE
        /* map the control page at its virtual address */
@@ -138,15 +171,25 @@ relocate_kernel:
 
 relocate_new_kernel:
        /* read the arguments and say goodbye to the stack */
-       movl  4(%esp), %ebx /* page_list */
-       movl  8(%esp), %ebp /* list of pages */
-       movl  12(%esp), %edx /* start address */
-       movl  16(%esp), %ecx /* cpu_has_pae */
+       movl  20+4(%esp), %ebx /* page_list */
+       movl  20+8(%esp), %ebp /* list of pages */
+       movl  20+12(%esp), %edx /* start address */
+       movl  20+16(%esp), %ecx /* cpu_has_pae */
+       movl  20+20(%esp), %esi /* preserve_context */
 
        /* zero out flags, and disable interrupts */
        pushl $0
        popfl
 
+       /* save some information for jumping back */
+       movl    PTR(VA_CONTROL_PAGE)(%ebp), %edi
+       movl    %edi, CP_VA_CONTROL_PAGE(%edi)
+       movl    PTR(PA_PGD)(%ebp), %eax
+       movl    %eax, CP_PA_PGD(%edi)
+       movl    PTR(PA_SWAP_PAGE)(%ebp), %eax
+       movl    %eax, CP_PA_SWAP_PAGE(%edi)
+       movl    %ebx, CP_PA_BACKUP_PAGES_MAP(%edi)
+
        /* get physical address of control page now */
        /* this is impossible after page table switch */
        movl    PTR(PA_CONTROL_PAGE)(%ebp), %edi
@@ -197,8 +240,90 @@ identity_mapped:
        xorl    %eax, %eax
        movl    %eax, %cr3
 
+       movl    CP_PA_SWAP_PAGE(%edi), %eax
+       pushl   %eax
+       pushl   %ebx
+       call    swap_pages
+       addl    $8, %esp
+
+       /* To be certain of avoiding problems with self-modifying code
+        * I need to execute a serializing instruction here.
+        * So I flush the TLB, it's handy, and not processor dependent.
+        */
+       xorl    %eax, %eax
+       movl    %eax, %cr3
+
+       /* set all of the registers to known values */
+       /* leave %esp alone */
+
+       testl   %esi, %esi
+       jnz 1f
+       xorl    %edi, %edi
+       xorl    %eax, %eax
+       xorl    %ebx, %ebx
+       xorl    %ecx, %ecx
+       xorl    %edx, %edx
+       xorl    %esi, %esi
+       xorl    %ebp, %ebp
+       ret
+1:
+       popl    %edx
+       movl    CP_PA_SWAP_PAGE(%edi), %esp
+       addl    $PAGE_SIZE, %esp
+2:
+       call    *%edx
+
+       /* get the re-entry point of the peer system */
+       movl    0(%esp), %ebp
+       call    1f
+1:
+       popl    %ebx
+       subl    $(1b - relocate_kernel), %ebx
+       movl    CP_VA_CONTROL_PAGE(%ebx), %edi
+       lea     PAGE_SIZE(%ebx), %esp
+       movl    CP_PA_SWAP_PAGE(%ebx), %eax
+       movl    CP_PA_BACKUP_PAGES_MAP(%ebx), %edx
+       pushl   %eax
+       pushl   %edx
+       call    swap_pages
+       addl    $8, %esp
+       movl    CP_PA_PGD(%ebx), %eax
+       movl    %eax, %cr3
+       movl    %cr0, %eax
+       orl     $(1<<31), %eax
+       movl    %eax, %cr0
+       lea     PAGE_SIZE(%edi), %esp
+       movl    %edi, %eax
+       addl    $(virtual_mapped - relocate_kernel), %eax
+       pushl   %eax
+       ret
+
+virtual_mapped:
+       movl    CR4(%edi), %eax
+       movl    %eax, %cr4
+       movl    CR3(%edi), %eax
+       movl    %eax, %cr3
+       movl    CR0(%edi), %eax
+       movl    %eax, %cr0
+       movl    ESP(%edi), %esp
+       movl    %ebp, %eax
+
+       popf
+       popl    %ebp
+       popl    %edi
+       popl    %esi
+       popl    %ebx
+       ret
+
        /* Do the copies */
-       movl    %ebx, %ecx
+swap_pages:
+       movl    8(%esp), %edx
+       movl    4(%esp), %ecx
+       pushl   %ebp
+       pushl   %ebx
+       pushl   %edi
+       pushl   %esi
+       movl    %ecx, %ebx
        jmp     1f
 
 0:     /* top, read another word from the indirection page */
@@ -226,27 +351,28 @@ identity_mapped:
        movl    %ecx,   %esi /* For every source page do a copy */
        andl    $0xfffff000, %esi
 
+       movl    %edi, %eax
+       movl    %esi, %ebp
+
+       movl    %edx, %edi
        movl    $1024, %ecx
        rep ; movsl
-       jmp     0b
 
-3:
-
-       /* To be certain of avoiding problems with self-modifying code
-        * I need to execute a serializing instruction here.
-        * So I flush the TLB, it's handy, and not processor dependent.
-        */
-       xorl    %eax, %eax
-       movl    %eax, %cr3
+       movl    %ebp, %edi
+       movl    %eax, %esi
+       movl    $1024, %ecx
+       rep ; movsl
 
-       /* set all of the registers to known values */
-       /* leave %esp alone */
+       movl    %eax, %edi
+       movl    %edx, %esi
+       movl    $1024, %ecx
+       rep ; movsl
 
-       xorl    %eax, %eax
-       xorl    %ebx, %ebx
-       xorl    %ecx, %ecx
-       xorl    %edx, %edx
-       xorl    %esi, %esi
-       xorl    %edi, %edi
-       xorl    %ebp, %ebp
+       lea     PAGE_SIZE(%ebp), %esi
+       jmp     0b
+3:
+       popl    %esi
+       popl    %edi
+       popl    %ebx
+       popl    %ebp
        ret