From: Rafael J. Wysocki <[EMAIL PROTECTED]>
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. It restores all of the image kernel's memory that has not been
restored yet except for the one page containing the very code that is being
executed at that time. 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 restores the last remaining page of the image
kernel's memory containing the first, already executed, part of the core
restoration code (temporary page tables created by the boot kernel are used at
this stage). It also 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
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]>
---
arch/x86_64/kernel/suspend.c | 43 ++
arch/x86_64/kernel/suspend_asm.S | 63 +--
include/asm-x86_64/suspend.h |6 +++
3 files changed, 103 insertions(+), 9 deletions(-)
Index: linux-2.6.23-rc3/arch/x86_64/kernel/suspend_asm.S
===
--- linux-2.6.23-rc3.orig/arch/x86_64/kernel/suspend_asm.S 2007-08-21
20:36:49.0 +0200
+++ linux-2.6.23-rc3/arch/x86_64/kernel/suspend_asm.S 2007-08-21
21:16:01.0 +0200
@@ -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,10 +36,20 @@ ENTRY(swsusp_arch_suspend)
pushfq
popqpt_regs_eflags(%rax)
+ /* save the address of restore_registers */
+ movq$restore_registers, %rax
+ movq%rax, restore_jump_address(%rip)
+
call swsusp_save
ret
ENTRY(restore_image)
+ /* compute the address of the page we are at and store it in R9 */
+ movq$(restore_image - __START_KERNEL_map), %rax
+ movq$__PAGE_OFFSET, %r9
+ addq%rax, %r9
+ andq$PAGE_MASK, %r9
+
/* switch to temporary page tables */
movq$__PAGE_OFFSET, %rdx
movqtemp_level4_pgt(%rip), %rax
@@ -54,6 +64,11 @@ ENTRY(restore_image)
movq%rcx, %cr3;
movq%rax, %cr4; # turn PGE back on
+ /* prepare to jump to the image kernel */
+ movqrestore_jump_address(%rip), %rax
+
+ /* copy image data to their original locations */
+ xorq%r10, %r10
movqrestore_pblist(%rip), %rdx
loop:
testq %rdx, %rdx
@@ -62,16 +77,46 @@ loop:
/* get addresses from the pbe and copy the page */
movqpbe_address(%rdx), %rsi
movqpbe_orig_address(%rdx), %rdi
- movq$512, %rcx
+ /* skip the page we are at (address stored in R9) */
+ cmpq%rdi, %r9
+ jne 1f
+ /* save the address of the data to be copied to the skipped page */
+ movq%rsi, %r10
+ jmp 2f
+1: movq$(PAGE_SIZE >> 3), %rcx
rep
movsq
/* progress to the next pbe */
- movqpbe_next(%rdx), %rdx
+2: movqpbe_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