Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=d158cbdf39ffaec9dd5299fdfdfdd2c7897a71dc
Commit:     d158cbdf39ffaec9dd5299fdfdfdd2c7897a71dc
Parent:     d307c4a8e826c44f9633bd3f7e60d0491e7d885a
Author:     Rafael J. Wysocki <[EMAIL PROTECTED]>
AuthorDate: Thu Oct 18 03:04:53 2007 -0700
Committer:  Linus Torvalds <[EMAIL PROTECTED]>
CommitDate: Thu Oct 18 14:37:19 2007 -0700

    Hibernation: Arbitrary boot kernel support on x86_64
    
    Make it possible to restore a hibernation image on x86_64 with the help of a
    kernel different from the one in the image.
    
    The idea is to split the core restoration code into two separate parts and 
to
    place each of them in a different page.   The first part belongs to the boot
    kernel and is executed as the last step of the image kernel's memory
    restoration procedure.   Before being executed, it is relocated to a safe 
page
    that won't be overwritten while copying the image kernel pages.
    
    The final operation performed by it is a jump to the second part of the core
    restoration code that belongs to the image kernel and has just been 
restored.
    This code makes the CPU switch to the image kernel's page tables and 
restores
    the state of general purpose registers (including the stack pointer) from
    before the hibernation.
    
    The main issue with this idea is that in order to jump to the second part of
    the core restoration code the boot kernel needs to know its address.
     However, this address may be passed to it in the image header.   Namely, 
the
    part of the image header previously used for checking if the version of the
    image kernel is correct can be replaced with some architecture specific data
    that will allow the boot kernel to jump to the right address within the 
image
    kernel.   These data should also be used for checking if the image kernel is
    compatible with the boot kernel (as far as the memory restroration procedure
    is concerned).  It can be done, for example, with the help of a "magic" 
value
    that has to be equal in both kernels, so that they can be regarded as
    compatible.
    
    Signed-off-by: Rafael J. Wysocki <[EMAIL PROTECTED]>
    Acked-by: Pavel Machek <[EMAIL PROTECTED]>
    Cc: Andi Kleen <[EMAIL PROTECTED]>
    Signed-off-by: Andrew Morton <[EMAIL PROTECTED]>
    Signed-off-by: Linus Torvalds <[EMAIL PROTECTED]>
---
 arch/x86/kernel/suspend_64.c     |   54 +++++++++++++++++++++++++++++++++++++-
 arch/x86/kernel/suspend_asm_64.S |   41 ++++++++++++++++++++++++-----
 arch/x86_64/Kconfig              |    5 +++
 include/asm-x86/suspend_64.h     |    2 +
 4 files changed, 94 insertions(+), 8 deletions(-)

diff --git a/arch/x86/kernel/suspend_64.c b/arch/x86/kernel/suspend_64.c
index 573c0a6..01fbfb0 100644
--- a/arch/x86/kernel/suspend_64.c
+++ b/arch/x86/kernel/suspend_64.c
@@ -150,8 +150,16 @@ void fix_processor_context(void)
 /* Defined in arch/x86_64/kernel/suspend_asm.S */
 extern int restore_image(void);
 
+/*
+ * Address to jump to in the last phase of restore in order to get to the image
+ * kernel's text (this value is passed in the image header).
+ */
+unsigned long restore_jump_address;
+
 pgd_t *temp_level4_pgt;
 
+void *relocated_restore_code;
+
 static int res_phys_pud_init(pud_t *pud, unsigned long address, unsigned long 
end)
 {
        long i, j;
@@ -175,7 +183,7 @@ static int res_phys_pud_init(pud_t *pud, unsigned long 
address, unsigned long en
 
                        if (paddr >= end)
                                break;
-                       pe = _PAGE_NX | _PAGE_PSE | _KERNPG_TABLE | paddr;
+                       pe = __PAGE_KERNEL_LARGE_EXEC | paddr;
                        pe &= __supported_pte_mask;
                        set_pmd(pmd, __pmd(pe));
                }
@@ -222,6 +230,13 @@ int swsusp_arch_resume(void)
        /* We have got enough memory and from now on we cannot recover */
        if ((error = set_up_temporary_mappings()))
                return error;
+
+       relocated_restore_code = (void *)get_safe_page(GFP_ATOMIC);
+       if (!relocated_restore_code)
+               return -ENOMEM;
+       memcpy(relocated_restore_code, &core_restore_code,
+              &restore_registers - &core_restore_code);
+
        restore_image();
        return 0;
 }
@@ -236,4 +251,41 @@ int pfn_is_nosave(unsigned long pfn)
        unsigned long nosave_end_pfn = PAGE_ALIGN(__pa_symbol(&__nosave_end)) 
>> PAGE_SHIFT;
        return (pfn >= nosave_begin_pfn) && (pfn < nosave_end_pfn);
 }
+
+struct restore_data_record {
+       unsigned long jump_address;
+       unsigned long control;
+};
+
+#define RESTORE_MAGIC  0x0123456789ABCDEFUL
+
+/**
+ *     arch_hibernation_header_save - populate the architecture specific part
+ *             of a hibernation image header
+ *     @addr: address to save the data at
+ */
+int arch_hibernation_header_save(void *addr, unsigned int max_size)
+{
+       struct restore_data_record *rdr = addr;
+
+       if (max_size < sizeof(struct restore_data_record))
+               return -EOVERFLOW;
+       rdr->jump_address = restore_jump_address;
+       rdr->control = (restore_jump_address ^ RESTORE_MAGIC);
+       return 0;
+}
+
+/**
+ *     arch_hibernation_header_restore - read the architecture specific data
+ *             from the hibernation image header
+ *     @addr: address to read the data from
+ */
+int arch_hibernation_header_restore(void *addr)
+{
+       struct restore_data_record *rdr = addr;
+
+       restore_jump_address = rdr->jump_address;
+       return (rdr->control == (restore_jump_address ^ RESTORE_MAGIC)) ?
+                       0 : -EINVAL;
+}
 #endif /* CONFIG_HIBERNATION */
diff --git a/arch/x86/kernel/suspend_asm_64.S b/arch/x86/kernel/suspend_asm_64.S
index 16d183f..40a209e 100644
--- a/arch/x86/kernel/suspend_asm_64.S
+++ b/arch/x86/kernel/suspend_asm_64.S
@@ -2,8 +2,8 @@
  *
  * Distribute under GPLv2.
  *
- * swsusp_arch_resume may not use any stack, nor any variable that is
- * not "NoSave" during copying pages:
+ * swsusp_arch_resume must not use any stack or any nonlocal variables while
+ * copying pages:
  *
  * Its rewriting one kernel image with another. What is stack in "old"
  * image could very well be data page in "new" image, and overwriting
@@ -36,6 +36,10 @@ ENTRY(swsusp_arch_suspend)
        movq %r15, saved_context_r15(%rip)
        pushfq ; popq saved_context_eflags(%rip)
 
+       /* save the address of restore_registers */
+       movq    $restore_registers, %rax
+       movq    %rax, restore_jump_address(%rip)
+
        call swsusp_save
        ret
 
@@ -54,7 +58,16 @@ ENTRY(restore_image)
        movq    %rcx, %cr3;
        movq    %rax, %cr4;  # turn PGE back on
 
+       /* prepare to jump to the image kernel */
+       movq    restore_jump_address(%rip), %rax
+
+       /* prepare to copy image data to their original locations */
        movq    restore_pblist(%rip), %rdx
+       movq    relocated_restore_code(%rip), %rcx
+       jmpq    *%rcx
+
+       /* code below has been relocated to a safe page */
+ENTRY(core_restore_code)
 loop:
        testq   %rdx, %rdx
        jz      done
@@ -62,7 +75,7 @@ loop:
        /* get addresses from the pbe and copy the page */
        movq    pbe_address(%rdx), %rsi
        movq    pbe_orig_address(%rdx), %rdi
-       movq    $512, %rcx
+       movq    $(PAGE_SIZE >> 3), %rcx
        rep
        movsq
 
@@ -70,6 +83,20 @@ loop:
        movq    pbe_next(%rdx), %rdx
        jmp     loop
 done:
+       /* jump to the restore_registers address from the image header */
+       jmpq    *%rax
+       /*
+        * NOTE: This assumes that the boot kernel's text mapping covers the
+        * image kernel's page containing restore_registers and the address of
+        * this page is the same as in the image kernel's text mapping (it
+        * should always be true, because the text mapping is linear, starting
+        * from 0, and is supposed to cover the entire kernel text for every
+        * kernel).
+        *
+        * code below belongs to the image kernel
+        */
+
+ENTRY(restore_registers)
        /* go back to the original page tables */
        movq    $(init_level4_pgt - __START_KERNEL_map), %rax
        addq    phys_base(%rip), %rax
@@ -84,12 +111,9 @@ done:
        movq    %rcx, %cr3
        movq    %rax, %cr4;  # turn PGE back on
 
-       movl    $24, %eax
-       movl    %eax, %ds
-
        movq saved_context_esp(%rip), %rsp
        movq saved_context_ebp(%rip), %rbp
-       /* Don't restore %rax, it must be 0 anyway */
+       /* restore GPRs (we don't restore %rax, it must be 0 anyway) */
        movq saved_context_ebx(%rip), %rbx
        movq saved_context_ecx(%rip), %rcx
        movq saved_context_edx(%rip), %rdx
@@ -107,4 +131,7 @@ done:
 
        xorq    %rax, %rax
 
+       /* tell the hibernation core that we've just restored the memory */
+       movq    %rax, in_suspend(%rip)
+
        ret
diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig
index 43fafe9..78cb68f 100644
--- a/arch/x86_64/Kconfig
+++ b/arch/x86_64/Kconfig
@@ -716,6 +716,11 @@ menu "Power management options"
 
 source kernel/power/Kconfig
 
+config ARCH_HIBERNATION_HEADER
+       bool
+       depends on HIBERNATION
+       default y
+
 source "drivers/acpi/Kconfig"
 
 source "arch/x86/kernel/cpufreq/Kconfig"
diff --git a/include/asm-x86/suspend_64.h b/include/asm-x86/suspend_64.h
index b897e8c..9440a7a 100644
--- a/include/asm-x86/suspend_64.h
+++ b/include/asm-x86/suspend_64.h
@@ -53,3 +53,5 @@ extern unsigned long saved_rdi;
 
 /* routines for saving/restoring kernel state */
 extern int acpi_save_state_mem(void);
+extern char core_restore_code;
+extern char restore_registers;
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to