From 7be5c55af0cc58e54e42e1702d837527e15b8414 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 18 Mar 2009 08:47:31 +0000 Subject: sh: simplify kexec vbr code Setup the vbr register in machine_kexec(). This instead of passing values to the assembly snippet. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- arch/sh/kernel/machine_kexec.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'arch/sh/kernel/machine_kexec.c') diff --git a/arch/sh/kernel/machine_kexec.c b/arch/sh/kernel/machine_kexec.c index 94df56b0d1f6..d3318f99256b 100644 --- a/arch/sh/kernel/machine_kexec.c +++ b/arch/sh/kernel/machine_kexec.c @@ -23,8 +23,7 @@ typedef NORET_TYPE void (*relocate_new_kernel_t)( unsigned long indirection_page, unsigned long reboot_code_buffer, - unsigned long start_address, - unsigned long vbr_reg) ATTRIB_NORET; + unsigned long start_address) ATTRIB_NORET; extern const unsigned char relocate_new_kernel[]; extern const unsigned int relocate_new_kernel_size; @@ -76,14 +75,8 @@ void machine_kexec(struct kimage *image) unsigned long page_list; unsigned long reboot_code_buffer; - unsigned long vbr_reg; relocate_new_kernel_t rnk; -#if defined(CONFIG_SH_STANDARD_BIOS) - vbr_reg = ((unsigned long )gdb_vbr_vector) - 0x100; -#else - vbr_reg = 0x80000000; // dummy -#endif /* Interrupts aren't acceptable while we reboot */ local_irq_disable(); @@ -100,9 +93,15 @@ void machine_kexec(struct kimage *image) kexec_info(image); flush_cache_all(); + set_bl_bit(); +#if defined(CONFIG_SH_STANDARD_BIOS) + asm volatile("ldc %0, vbr" : + : "r" (((unsigned long) gdb_vbr_vector) - 0x100) + : "memory"); +#endif /* now call it */ rnk = (relocate_new_kernel_t) reboot_code_buffer; - (*rnk)(page_list, reboot_code_buffer, P2SEGADDR(image->start), vbr_reg); + (*rnk)(page_list, reboot_code_buffer, P2SEGADDR(image->start)); } void arch_crash_save_vmcoreinfo(void) -- cgit v1.2.3 From e4e063d0c288bd65c56dd855337780a541ed928d Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 18 Mar 2009 08:49:45 +0000 Subject: sh: rework kexec segment code Rework the kexec code to avoid using P2SEG. Instead we walk the page list in machine_kexec() and convert the addresses from physical to virtual using C. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- arch/sh/kernel/machine_kexec.c | 17 ++++++++++++++++- arch/sh/kernel/relocate_kernel.S | 6 +----- 2 files changed, 17 insertions(+), 6 deletions(-) (limited to 'arch/sh/kernel/machine_kexec.c') diff --git a/arch/sh/kernel/machine_kexec.c b/arch/sh/kernel/machine_kexec.c index d3318f99256b..25b4748fdc7b 100644 --- a/arch/sh/kernel/machine_kexec.c +++ b/arch/sh/kernel/machine_kexec.c @@ -76,6 +76,21 @@ void machine_kexec(struct kimage *image) unsigned long page_list; unsigned long reboot_code_buffer; relocate_new_kernel_t rnk; + unsigned long entry; + unsigned long *ptr; + + /* + * Nicked from the mips version of machine_kexec(): + * The generic kexec code builds a page list with physical + * addresses. Use phys_to_virt() to convert them to virtual. + */ + for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE); + ptr = (entry & IND_INDIRECTION) ? + phys_to_virt(entry & PAGE_MASK) : ptr + 1) { + if (*ptr & IND_SOURCE || *ptr & IND_INDIRECTION || + *ptr & IND_DESTINATION) + *ptr = (unsigned long) phys_to_virt(*ptr); + } /* Interrupts aren't acceptable while we reboot */ local_irq_disable(); @@ -101,7 +116,7 @@ void machine_kexec(struct kimage *image) #endif /* now call it */ rnk = (relocate_new_kernel_t) reboot_code_buffer; - (*rnk)(page_list, reboot_code_buffer, P2SEGADDR(image->start)); + (*rnk)(page_list, reboot_code_buffer, image->start); } void arch_crash_save_vmcoreinfo(void) diff --git a/arch/sh/kernel/relocate_kernel.S b/arch/sh/kernel/relocate_kernel.S index 8b50b2c873a4..2a6630be668c 100644 --- a/arch/sh/kernel/relocate_kernel.S +++ b/arch/sh/kernel/relocate_kernel.S @@ -18,7 +18,6 @@ relocate_new_kernel: /* r6 = start_address */ mov.l 10f,r8 /* PAGE_SIZE */ - mov.l 11f,r9 /* P2SEG */ /* stack setting */ add r8,r5 @@ -29,9 +28,8 @@ relocate_new_kernel: 0: mov.l @r4+,r0 /* cmd = *ind++ */ -1: /* addr = (cmd | P2SEG) & 0xfffffff0 */ +1: /* addr = cmd & 0xfffffff0 */ mov r0,r2 - or r9,r2 mov #-16,r1 and r1,r2 @@ -85,8 +83,6 @@ relocate_new_kernel: .align 2 10: .long PAGE_SIZE -11: - .long P2SEG relocate_new_kernel_end: -- cgit v1.2.3 From b7cf6ddc13186f9272438a97aa75972d496d0b0a Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 18 Mar 2009 08:51:29 +0000 Subject: sh: add kexec jump support Add kexec jump support to the SuperH architecture. Similar to the x86 implementation, with the following exceptions: - Instead of separating the assembly code flow into two parts for regular kexec and kexec jump we use a single code path. In the assembly snippet regular kexec is just kexec jump that never comes back. - Instead of using a swap page when moving data between pages the page copy assembly routine has been modified to exchange the data between the pages using registers. - We walk the page list twice in machine_kexec() to do and undo physical to virtual address conversion. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- arch/sh/Kconfig | 7 ++ arch/sh/kernel/machine_kexec.c | 32 ++++++- arch/sh/kernel/relocate_kernel.S | 195 +++++++++++++++++++++++++++++++++------ 3 files changed, 202 insertions(+), 32 deletions(-) (limited to 'arch/sh/kernel/machine_kexec.c') diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index 6c56495fd158..8d50d527c595 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -559,6 +559,13 @@ config CRASH_DUMP For more details see Documentation/kdump/kdump.txt +config KEXEC_JUMP + bool "kexec jump (EXPERIMENTAL)" + depends on SUPERH32 && KEXEC && HIBERNATION && EXPERIMENTAL + help + Jump between original kernel and kexeced kernel and invoke + code via KEXEC + config SECCOMP bool "Enable seccomp to safely compute untrusted bytecode" depends on PROC_FS diff --git a/arch/sh/kernel/machine_kexec.c b/arch/sh/kernel/machine_kexec.c index 25b4748fdc7b..c44efb73ab1a 100644 --- a/arch/sh/kernel/machine_kexec.c +++ b/arch/sh/kernel/machine_kexec.c @@ -14,20 +14,21 @@ #include #include #include +#include #include #include #include #include #include -typedef NORET_TYPE void (*relocate_new_kernel_t)( - unsigned long indirection_page, - unsigned long reboot_code_buffer, - unsigned long start_address) ATTRIB_NORET; +typedef void (*relocate_new_kernel_t)(unsigned long indirection_page, + unsigned long reboot_code_buffer, + unsigned long start_address); extern const unsigned char relocate_new_kernel[]; extern const unsigned int relocate_new_kernel_size; extern void *gdb_vbr_vector; +extern void *vbr_base; void machine_shutdown(void) { @@ -72,7 +73,6 @@ static void kexec_info(struct kimage *image) */ void machine_kexec(struct kimage *image) { - unsigned long page_list; unsigned long reboot_code_buffer; relocate_new_kernel_t rnk; @@ -92,6 +92,11 @@ void machine_kexec(struct kimage *image) *ptr = (unsigned long) phys_to_virt(*ptr); } +#ifdef CONFIG_KEXEC_JUMP + if (image->preserve_context) + save_processor_state(); +#endif + /* Interrupts aren't acceptable while we reboot */ local_irq_disable(); @@ -117,6 +122,23 @@ void machine_kexec(struct kimage *image) /* now call it */ rnk = (relocate_new_kernel_t) reboot_code_buffer; (*rnk)(page_list, reboot_code_buffer, image->start); + +#ifdef CONFIG_KEXEC_JUMP + asm volatile("ldc %0, vbr" : : "r" (&vbr_base) : "memory"); + local_irq_disable(); + clear_bl_bit(); + if (image->preserve_context) + restore_processor_state(); + + /* Convert page list back to physical addresses, what a mess. */ + for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE); + ptr = (*ptr & IND_INDIRECTION) ? + phys_to_virt(*ptr & PAGE_MASK) : ptr + 1) { + if (*ptr & IND_SOURCE || *ptr & IND_INDIRECTION || + *ptr & IND_DESTINATION) + *ptr = virt_to_phys(*ptr); + } +#endif } void arch_crash_save_vmcoreinfo(void) diff --git a/arch/sh/kernel/relocate_kernel.S b/arch/sh/kernel/relocate_kernel.S index 2a6630be668c..fcc9934fb97b 100644 --- a/arch/sh/kernel/relocate_kernel.S +++ b/arch/sh/kernel/relocate_kernel.S @@ -4,6 +4,8 @@ * * LANDISK/sh4 is supported. Maybe, SH archtecture works well. * + * 2009-03-18 Magnus Damm - Added Kexec Jump support + * * This source code is licensed under the GNU General Public License, * Version 2. See the file COPYING for more details. */ @@ -17,14 +19,135 @@ relocate_new_kernel: /* r5 = reboot_code_buffer */ /* r6 = start_address */ - mov.l 10f,r8 /* PAGE_SIZE */ + mov.l 10f, r0 /* PAGE_SIZE */ + add r5, r0 /* setup new stack at end of control page */ + + /* save r15->r8 to new stack */ + mov.l r15, @-r0 + mov r0, r15 + mov.l r14, @-r15 + mov.l r13, @-r15 + mov.l r12, @-r15 + mov.l r11, @-r15 + mov.l r10, @-r15 + mov.l r9, @-r15 + mov.l r8, @-r15 + + /* save other random registers */ + sts.l macl, @-r15 + sts.l mach, @-r15 + stc.l gbr, @-r15 + stc.l ssr, @-r15 + stc.l sr, @-r15 + sts.l pr, @-r15 + stc.l spc, @-r15 + + /* switch to bank1 and save r7->r0 */ + mov.l 12f, r9 + stc sr, r8 + or r9, r8 + ldc r8, sr + mov.l r7, @-r15 + mov.l r6, @-r15 + mov.l r5, @-r15 + mov.l r4, @-r15 + mov.l r3, @-r15 + mov.l r2, @-r15 + mov.l r1, @-r15 + mov.l r0, @-r15 + + /* switch to bank0 and save r7->r0 */ + mov.l 12f, r9 + not r9, r9 + stc sr, r8 + and r9, r8 + ldc r8, sr + mov.l r7, @-r15 + mov.l r6, @-r15 + mov.l r5, @-r15 + mov.l r4, @-r15 + mov.l r3, @-r15 + mov.l r2, @-r15 + mov.l r1, @-r15 + mov.l r0, @-r15 + + mov.l r4, @-r15 /* save indirection page again */ + + bsr swap_pages /* swap pages before jumping to new kernel */ + nop + + mova 11f, r0 + mov.l r15, @r0 /* save pointer to stack */ + + jsr @r6 /* hand over control to new kernel */ + nop + + mov.l 11f, r15 /* get pointer to stack */ + mov.l @r15+, r4 /* restore r4 to get indirection page */ - /* stack setting */ - add r8,r5 - mov r5,r15 + bsr swap_pages /* swap pages back to previous state */ + nop + /* make sure bank0 is active and restore r0->r7 */ + mov.l 12f, r9 + not r9, r9 + stc sr, r8 + and r9, r8 + ldc r8, sr + mov.l @r15+, r0 + mov.l @r15+, r1 + mov.l @r15+, r2 + mov.l @r15+, r3 + mov.l @r15+, r4 + mov.l @r15+, r5 + mov.l @r15+, r6 + mov.l @r15+, r7 + + /* switch to bank1 and restore r0->r7 */ + mov.l 12f, r9 + stc sr, r8 + or r9, r8 + ldc r8, sr + mov.l @r15+, r0 + mov.l @r15+, r1 + mov.l @r15+, r2 + mov.l @r15+, r3 + mov.l @r15+, r4 + mov.l @r15+, r5 + mov.l @r15+, r6 + mov.l @r15+, r7 + + /* switch back to bank0 */ + mov.l 12f, r9 + not r9, r9 + stc sr, r8 + and r9, r8 + ldc r8, sr + + /* restore other random registers */ + ldc.l @r15+, spc + lds.l @r15+, pr + ldc.l @r15+, sr + ldc.l @r15+, ssr + ldc.l @r15+, gbr + lds.l @r15+, mach + lds.l @r15+, macl + + /* restore r8->r15 */ + mov.l @r15+, r8 + mov.l @r15+, r9 + mov.l @r15+, r10 + mov.l @r15+, r11 + mov.l @r15+, r12 + mov.l @r15+, r13 + mov.l @r15+, r14 + mov.l @r15+, r15 + rts + nop + +swap_pages: bra 1f - mov r4,r0 /* cmd = indirection_page */ + mov r4,r0 /* cmd = indirection_page */ 0: mov.l @r4+,r0 /* cmd = *ind++ */ @@ -37,52 +160,70 @@ relocate_new_kernel: tst #1,r0 bt 2f bra 0b - mov r2,r5 + mov r2,r5 2: /* else if(cmd & IND_INDIRECTION) ind = addr */ tst #2,r0 bt 3f bra 0b - mov r2,r4 + mov r2,r4 -3: /* else if(cmd & IND_DONE) goto 6 */ +3: /* else if(cmd & IND_DONE) return */ tst #4,r0 bt 4f - bra 6f - nop + rts + nop 4: /* else if(cmd & IND_SOURCE) memcpy(dst,addr,PAGE_SIZE) */ tst #8,r0 bt 0b - mov r8,r3 + mov.l 10f,r3 /* PAGE_SIZE */ shlr2 r3 shlr2 r3 5: dt r3 - mov.l @r2+,r1 /* 16n+0 */ - mov.l r1,@r5 - add #4,r5 - mov.l @r2+,r1 /* 16n+4 */ - mov.l r1,@r5 - add #4,r5 - mov.l @r2+,r1 /* 16n+8 */ - mov.l r1,@r5 - add #4,r5 - mov.l @r2+,r1 /* 16n+12 */ - mov.l r1,@r5 - add #4,r5 + + /* regular kexec just overwrites the destination page + * with the contents of the source page. + * for the kexec jump case we need to swap the contents + * of the pages. + * to keep it simple swap the contents for both cases. + */ + mov.l @(0, r2), r8 + mov.l @(0, r5), r1 + mov.l r8, @(0, r5) + mov.l r1, @(0, r2) + + mov.l @(4, r2), r8 + mov.l @(4, r5), r1 + mov.l r8, @(4, r5) + mov.l r1, @(4, r2) + + mov.l @(8, r2), r8 + mov.l @(8, r5), r1 + mov.l r8, @(8, r5) + mov.l r1, @(8, r2) + + mov.l @(12, r2), r8 + mov.l @(12, r5), r1 + mov.l r8, @(12, r5) + mov.l r1, @(12, r2) + + add #16,r5 + add #16,r2 bf 5b bra 0b - nop -6: - jmp @r6 - nop + nop .align 2 10: .long PAGE_SIZE +11: + .long 0 +12: + .long 0x20000000 ! RB=1 relocate_new_kernel_end: -- cgit v1.2.3 From a6bab7b5c18501e4dd3201ae8ac1dc6da5f07acc Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 18 Mar 2009 19:06:15 +0900 Subject: sh: kexec: Drop SR.BL bit toggling. For the time being, this creates far more problems than it solves, evident by the second local_irq_disable(). Kill all of this off and rely on IRQ disabling to protect against the VBR reload. Signed-off-by: Paul Mundt --- arch/sh/kernel/machine_kexec.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'arch/sh/kernel/machine_kexec.c') diff --git a/arch/sh/kernel/machine_kexec.c b/arch/sh/kernel/machine_kexec.c index c44efb73ab1a..69268c0d8063 100644 --- a/arch/sh/kernel/machine_kexec.c +++ b/arch/sh/kernel/machine_kexec.c @@ -110,23 +110,22 @@ void machine_kexec(struct kimage *image) memcpy((void *)reboot_code_buffer, relocate_new_kernel, relocate_new_kernel_size); - kexec_info(image); + kexec_info(image); flush_cache_all(); - set_bl_bit(); #if defined(CONFIG_SH_STANDARD_BIOS) asm volatile("ldc %0, vbr" : : "r" (((unsigned long) gdb_vbr_vector) - 0x100) : "memory"); #endif + /* now call it */ rnk = (relocate_new_kernel_t) reboot_code_buffer; (*rnk)(page_list, reboot_code_buffer, image->start); #ifdef CONFIG_KEXEC_JUMP asm volatile("ldc %0, vbr" : : "r" (&vbr_base) : "memory"); - local_irq_disable(); - clear_bl_bit(); + if (image->preserve_context) restore_processor_state(); -- cgit v1.2.3 From 7e6b6f2b949a52382f59a93ecbe86e32e4fcec7c Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 18 Mar 2009 19:07:16 +0900 Subject: sh: kexec jump: fix for ftrace. Save and restore ftrace state when returning from kexec jump in machine_kexec(). Follows the x86 change. Signed-off-by: Paul Mundt --- arch/sh/kernel/machine_kexec.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'arch/sh/kernel/machine_kexec.c') diff --git a/arch/sh/kernel/machine_kexec.c b/arch/sh/kernel/machine_kexec.c index 69268c0d8063..cc7c29b0dc8d 100644 --- a/arch/sh/kernel/machine_kexec.c +++ b/arch/sh/kernel/machine_kexec.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -78,6 +79,7 @@ void machine_kexec(struct kimage *image) relocate_new_kernel_t rnk; unsigned long entry; unsigned long *ptr; + int save_ftrace_enabled; /* * Nicked from the mips version of machine_kexec(): @@ -97,6 +99,8 @@ void machine_kexec(struct kimage *image) save_processor_state(); #endif + save_ftrace_enabled = __ftrace_enabled_save(); + /* Interrupts aren't acceptable while we reboot */ local_irq_disable(); @@ -138,6 +142,8 @@ void machine_kexec(struct kimage *image) *ptr = virt_to_phys(*ptr); } #endif + + __ftrace_enabled_restore(save_ftrace_enabled); } void arch_crash_save_vmcoreinfo(void) -- cgit v1.2.3 From 615e73b3cd8876262f61ea28b4147c8de38a043a Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Thu, 19 Mar 2009 10:04:29 +0000 Subject: sh: disallow kexec virtual entry Older versions of kexec-tools has a zImage loader that passes a virtual address as entry point. The elf loader otoh it passes a physical address as entry point, and pages are always passed as physical addresses as well. Only allow physical addresses from now on. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- arch/sh/kernel/machine_kexec.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'arch/sh/kernel/machine_kexec.c') diff --git a/arch/sh/kernel/machine_kexec.c b/arch/sh/kernel/machine_kexec.c index cc7c29b0dc8d..7ea2704ea033 100644 --- a/arch/sh/kernel/machine_kexec.c +++ b/arch/sh/kernel/machine_kexec.c @@ -46,6 +46,12 @@ void machine_crash_shutdown(struct pt_regs *regs) */ int machine_kexec_prepare(struct kimage *image) { + /* older versions of kexec-tools are passing + * the zImage entry point as a virtual address. + */ + if (image->start != PHYSADDR(image->start)) + return -EINVAL; /* upgrade your kexec-tools */ + return 0; } @@ -125,7 +131,8 @@ void machine_kexec(struct kimage *image) /* now call it */ rnk = (relocate_new_kernel_t) reboot_code_buffer; - (*rnk)(page_list, reboot_code_buffer, image->start); + (*rnk)(page_list, reboot_code_buffer, + (unsigned long)phys_to_virt(image->start)); #ifdef CONFIG_KEXEC_JUMP asm volatile("ldc %0, vbr" : : "r" (&vbr_base) : "memory"); -- cgit v1.2.3