kexec based hibernation -v5: kexec jump
Subsystems (8)
PATCH 1/2 kexec based hibernation -v5: kexec jump
This patch implements the functionality of jumping between the kexeced kernel and the original kernel.
A new flag for sys_kexec_load named KEXEC_JUMP_BACK is added to indicate that the loaded kernel image is used for jumping back.
To support jumping between two kernels, before jumping to (executing) the new kernel and jumping back to the original kernel, the devices are put into quiescent state, and the state of devices and CPU is saved. After jumping back from kexeced kernel and jumping to the new kernel, the state of devices and CPU are restored accordingly. The devices/CPU state save/restore code of software suspend is called to implement corresponding function.
To support jumping without reserving memory. One shadow backup page (source page) is allocated for each page used by new (kexeced) kernel (destination page). When do kexec_load, the image of new kernel is loaded into source pages, and before executing, the destination pages and the source pages are swapped, so the contents of destination pages are backupped. Before jumping to the new (kexeced) kernel and after jumping back to the original kernel, the destination pages and the source pages are swapped too.
A jump back protocol is defined and documented.
---
arch/i386/kernel/machine_kexec.c +38 -6
@@ -20,6 +20,7 @@#include <asm/cpufeature.h> #include <asm/desc.h> #include <asm/system.h> +#include <asm/cacheflush.h> #define PAGE_ALIGNED __attribute__ ((__aligned__(PAGE_SIZE))) static u32 kexec_pgd[1024] PAGE_ALIGNED;
@@ -83,10 +84,14 @@* reboot code buffer to allow us to avoid allocations * later. * -* Currently nothing. +* Turn off NX bit for control page. */ int machine_kexec_prepare (struct kimage *image) { +if (nx_enabled) { +change_page_attr (image->control_code_page,1 ,PAGE_KERNEL_EXEC ); +global_flush_tlb (); + }return 0 ; }
@@ -96,25 +101,50 @@*/ void machine_kexec_cleanup (struct kimage *image) { +if (nx_enabled) { +change_page_attr (image->control_code_page,1 ,PAGE_KERNEL ); +global_flush_tlb (); + } }/* * Do not allocate memory (or fail in any way) in machine_kexec(). * We are past the point of no return, committed to rebooting now. */ -NORET_TYPE void machine_kexec(struct kimage *image) +void machine_kexec (struct kimage *image) {unsigned long page_list[PAGES_NR ];void *control_page; + asmlinkage NORET_TYPE void + (*relocate_kernel_ptr)(unsigned long indirection_page, + unsigned long control_page, + unsigned long start_address, + unsigned int has_pae) ATTRIB_NORET;/* Interrupts aren't acceptable while we reboot */ local_irq_disable (); control_page =page_address (image->control_code_page); -memcpy (control_page, relocate_kernel,PAGE_SIZE ); +memcpy (control_page, relocate_page,PAGE_SIZE /2 ); +if (image->jump_back) { +unsigned long pa_control_page; + pa_control_page =__pa (control_page); +memcpy (control_page +1 , &pa_control_page, +sizeof (pa_control_page)); +KJUMP_MAGIC (control_page) =KJUMP_MAGIC_NUMBER ; +KJUMP_VERSION (control_page) =KJUMP_VERSION_NUMBER ; + +if (kexec_jump_save_cpu (control_page)) { + image->start =KJUMP_ENTRY (control_page); +return ; + } + } + + relocate_kernel_ptr = control_page + + ((void *)relocate_kernel - (void *)relocate_page); page_list[PA_CONTROL_PAGE ] =__pa (control_page); - page_list[VA_CONTROL_PAGE ] = (unsigned long )relocate_kernel; + page_list[VA_CONTROL_PAGE ] = (unsigned long )control_page; page_list[PA_PGD ] =__pa (kexec_pgd); page_list[VA_PGD ] = (unsigned long )kexec_pgd;#ifdef CONFIG_X86_PAE
@@ -127,6 +157,7 @@ page_list[VA_PTE_0 ] = (unsigned long )kexec_pte0; page_list[PA_PTE_1 ] =__pa (kexec_pte1); page_list[VA_PTE_1 ] = (unsigned long )kexec_pte1; + page_list[PA_SWAP_PAGE ] = (page_to_pfn (image->swap_page) <<PAGE_SHIFT );/* The segment registers are funny things, they have both a * visible and an invisible part. Whenever the visible part is
@@ -145,8 +176,9 @@set_idt (phys_to_virt (0 ),0 );/* now call it */ -relocate_kernel ((unsigned long )image->head, (unsigned long )page_list, - image->start, cpu_has_pae); +relocate_kernel_ptr ((unsigned long )image->head, + (unsigned long )page_list, + image->start, cpu_has_pae); }void arch_crash_save_vmcoreinfo (void )
include/asm-i386/kexec.h +35 -6
57 lines not on this page · raw message
include/linux/kexec.h +8 -2
@@ -83,6 +83,7 @@unsigned long start;struct page *control_code_page; +struct page *swap_page;unsigned long nr_segments;struct kexec_segment segment[KEXEC_SEGMENT_MAX ];
@@ -98,18 +99,21 @@ unsigned int type : 1;#define KEXEC_TYPE_DEFAULT 0#define KEXEC_TYPE_CRASH 1 + unsigned int jump_back : 1; };/* kexec interface functions */ -extern NORET_TYPE void machine_kexec(struct kimage *image) ATTRIB_NORET; +extern void machine_kexec (struct kimage *image);extern int machine_kexec_prepare (struct kimage *image);extern void machine_kexec_cleanup (struct kimage *image);extern asmlinkage long sys_kexec_load (unsigned long entry,unsigned long nr_segments,struct kexec_segment __user *segments,unsigned long flags); +extern void kimage_free (struct kimage *image); +extern int kexec_jump (struct kimage *image);#ifdef CONFIG_COMPAT extern asmlinkage long compat_sys_kexec_load(unsigned long entry, unsigned long nr_segments,
@@ -153,6 +157,7 @@#endif #define KEXEC_ON_CRASH 0x00000001 +#define KEXEC_JUMP_BACK 0x00000002#define KEXEC_ARCH_MASK 0xffff0000/* These values match the ELF architecture values.
@@ -170,7 +175,8 @@#define KEXEC_ARCH_MIPS_LE (10 << 16)#define KEXEC_ARCH_MIPS ( 8 << 16) -#define KEXEC_FLAGS (KEXEC_ON_CRASH)/* List of defined/legal kexec flags */ +/* List of defined/legal kexec flags */ +#define KEXEC_FLAGS (KEXEC_ON_CRASH|KEXEC_JUMP_BACK)#define VMCOREINFO_BYTES (4096)#define VMCOREINFO_NOTE_NAME "VMCOREINFO"
kernel/kexec.c +55 -1
83 lines not on this page · raw message
kernel/sys.c +16 -5
@@ -300,17 +300,25 @@* Move into place and start executing a preloaded standalone * executable. If nothing was preloaded return an error. */ -static void kernel_kexec (void ) +static int kernel_kexec (void ) {#ifdef CONFIG_KEXEC struct kimage *image; +int ret; image =xchg (&kexec_image,NULL );if (!image) -return ; +return -EINVAL ; +if (image->jump_back) { + ret =kexec_jump (image); + image =xchg (&kexec_image, image); +kimage_free (image); +return ret; + }kernel_restart_prepare (NULL );printk (KERN_EMERG "Starting new kernel\n" );machine_shutdown ();machine_kexec (image); +return -EINVAL ;#endif }
@@ -418,9 +426,12 @@break ;case LINUX_REBOOT_CMD_KEXEC : -kernel_kexec (); -unlock_kernel (); -return -EINVAL ; + { +int ret; + ret =kernel_kexec (); +unlock_kernel (); +return ret; + }#ifdef CONFIG_HIBERNATION case LINUX_REBOOT_CMD_SW_SUSPEND :
kernel/power/Kconfig +1 -1
@@ -70,7 +70,7 @@ config PM_SLEEP bool - depends on SUSPEND || HIBERNATION + depends on SUSPEND || HIBERNATION || KEXEC default y config SUSPEND_UP_POSSIBLE
arch/i386/kernel/relocate_kernel.S +167 -20
227 lines not on this page · raw message
Documentation/i386/jump_back_protocol.txt new file +81 -0
81 lines not on this page · raw message
arch/ppc/kernel/machine_kexec.c +1 -1
@@ -66,7 +66,7 @@* Do not allocate memory (or fail in any way) in machine_kexec(). * We are past the point of no return, committed to rebooting now. */ -NORET_TYPE void machine_kexec(struct kimage *image) +void machine_kexec (struct kimage *image) {if (ppc_md.machine_kexec) ppc_md.machine_kexec (image);
arch/x86_64/kernel/machine_kexec.c +1 -1
@@ -179,7 +179,7 @@* Do not allocate memory (or fail in any way) in machine_kexec(). * We are past the point of no return, committed to rebooting now. */ -NORET_TYPE void machine_kexec(struct kimage *image) +void machine_kexec (struct kimage *image) {unsigned long page_list[PAGES_NR ];void *control_page;
arch/sh/kernel/machine_kexec.c +1 -1
@@ -70,7 +70,7 @@* Do not allocate memory (or fail in any way) in machine_kexec(). * We are past the point of no return, committed to rebooting now. */ -NORET_TYPE void machine_kexec(struct kimage *image) +void machine_kexec (struct kimage *image) {unsigned long page_list;
arch/powerpc/kernel/machine_kexec.c +1 -1
@@ -47,7 +47,7 @@* Do not allocate memory (or fail in any way) in machine_kexec(). * We are past the point of no return, committed to rebooting now. */ -NORET_TYPE void machine_kexec(struct kimage *image) +void machine_kexec (struct kimage *image) {if (ppc_md.machine_kexec) ppc_md.machine_kexec (image);