diff options
Diffstat (limited to 'arch/sh/kernel/machine_kexec.c')
| -rw-r--r-- | arch/sh/kernel/machine_kexec.c | 74 |
1 files changed, 61 insertions, 13 deletions
diff --git a/arch/sh/kernel/machine_kexec.c b/arch/sh/kernel/machine_kexec.c index 94df56b0d1f6..7ea2704ea033 100644 --- a/arch/sh/kernel/machine_kexec.c +++ b/arch/sh/kernel/machine_kexec.c | |||
| @@ -14,21 +14,22 @@ | |||
| 14 | #include <linux/delay.h> | 14 | #include <linux/delay.h> |
| 15 | #include <linux/reboot.h> | 15 | #include <linux/reboot.h> |
| 16 | #include <linux/numa.h> | 16 | #include <linux/numa.h> |
| 17 | #include <linux/ftrace.h> | ||
| 18 | #include <linux/suspend.h> | ||
| 17 | #include <asm/pgtable.h> | 19 | #include <asm/pgtable.h> |
| 18 | #include <asm/pgalloc.h> | 20 | #include <asm/pgalloc.h> |
| 19 | #include <asm/mmu_context.h> | 21 | #include <asm/mmu_context.h> |
| 20 | #include <asm/io.h> | 22 | #include <asm/io.h> |
| 21 | #include <asm/cacheflush.h> | 23 | #include <asm/cacheflush.h> |
| 22 | 24 | ||
| 23 | typedef NORET_TYPE void (*relocate_new_kernel_t)( | 25 | typedef void (*relocate_new_kernel_t)(unsigned long indirection_page, |
| 24 | unsigned long indirection_page, | 26 | unsigned long reboot_code_buffer, |
| 25 | unsigned long reboot_code_buffer, | 27 | unsigned long start_address); |
| 26 | unsigned long start_address, | ||
| 27 | unsigned long vbr_reg) ATTRIB_NORET; | ||
| 28 | 28 | ||
| 29 | extern const unsigned char relocate_new_kernel[]; | 29 | extern const unsigned char relocate_new_kernel[]; |
| 30 | extern const unsigned int relocate_new_kernel_size; | 30 | extern const unsigned int relocate_new_kernel_size; |
| 31 | extern void *gdb_vbr_vector; | 31 | extern void *gdb_vbr_vector; |
| 32 | extern void *vbr_base; | ||
| 32 | 33 | ||
| 33 | void machine_shutdown(void) | 34 | void machine_shutdown(void) |
| 34 | { | 35 | { |
| @@ -45,6 +46,12 @@ void machine_crash_shutdown(struct pt_regs *regs) | |||
| 45 | */ | 46 | */ |
| 46 | int machine_kexec_prepare(struct kimage *image) | 47 | int machine_kexec_prepare(struct kimage *image) |
| 47 | { | 48 | { |
| 49 | /* older versions of kexec-tools are passing | ||
| 50 | * the zImage entry point as a virtual address. | ||
| 51 | */ | ||
| 52 | if (image->start != PHYSADDR(image->start)) | ||
| 53 | return -EINVAL; /* upgrade your kexec-tools */ | ||
| 54 | |||
| 48 | return 0; | 55 | return 0; |
| 49 | } | 56 | } |
| 50 | 57 | ||
| @@ -73,17 +80,33 @@ static void kexec_info(struct kimage *image) | |||
| 73 | */ | 80 | */ |
| 74 | void machine_kexec(struct kimage *image) | 81 | void machine_kexec(struct kimage *image) |
| 75 | { | 82 | { |
| 76 | |||
| 77 | unsigned long page_list; | 83 | unsigned long page_list; |
| 78 | unsigned long reboot_code_buffer; | 84 | unsigned long reboot_code_buffer; |
| 79 | unsigned long vbr_reg; | ||
| 80 | relocate_new_kernel_t rnk; | 85 | relocate_new_kernel_t rnk; |
| 86 | unsigned long entry; | ||
| 87 | unsigned long *ptr; | ||
| 88 | int save_ftrace_enabled; | ||
| 89 | |||
| 90 | /* | ||
| 91 | * Nicked from the mips version of machine_kexec(): | ||
| 92 | * The generic kexec code builds a page list with physical | ||
| 93 | * addresses. Use phys_to_virt() to convert them to virtual. | ||
| 94 | */ | ||
| 95 | for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE); | ||
| 96 | ptr = (entry & IND_INDIRECTION) ? | ||
| 97 | phys_to_virt(entry & PAGE_MASK) : ptr + 1) { | ||
| 98 | if (*ptr & IND_SOURCE || *ptr & IND_INDIRECTION || | ||
| 99 | *ptr & IND_DESTINATION) | ||
| 100 | *ptr = (unsigned long) phys_to_virt(*ptr); | ||
| 101 | } | ||
| 81 | 102 | ||
| 82 | #if defined(CONFIG_SH_STANDARD_BIOS) | 103 | #ifdef CONFIG_KEXEC_JUMP |
| 83 | vbr_reg = ((unsigned long )gdb_vbr_vector) - 0x100; | 104 | if (image->preserve_context) |
| 84 | #else | 105 | save_processor_state(); |
| 85 | vbr_reg = 0x80000000; // dummy | ||
| 86 | #endif | 106 | #endif |
| 107 | |||
| 108 | save_ftrace_enabled = __ftrace_enabled_save(); | ||
| 109 | |||
| 87 | /* Interrupts aren't acceptable while we reboot */ | 110 | /* Interrupts aren't acceptable while we reboot */ |
| 88 | local_irq_disable(); | 111 | local_irq_disable(); |
| 89 | 112 | ||
| @@ -97,12 +120,37 @@ void machine_kexec(struct kimage *image) | |||
| 97 | memcpy((void *)reboot_code_buffer, relocate_new_kernel, | 120 | memcpy((void *)reboot_code_buffer, relocate_new_kernel, |
| 98 | relocate_new_kernel_size); | 121 | relocate_new_kernel_size); |
| 99 | 122 | ||
| 100 | kexec_info(image); | 123 | kexec_info(image); |
| 101 | flush_cache_all(); | 124 | flush_cache_all(); |
| 102 | 125 | ||
| 126 | #if defined(CONFIG_SH_STANDARD_BIOS) | ||
| 127 | asm volatile("ldc %0, vbr" : | ||
| 128 | : "r" (((unsigned long) gdb_vbr_vector) - 0x100) | ||
| 129 | : "memory"); | ||
| 130 | #endif | ||
| 131 | |||
| 103 | /* now call it */ | 132 | /* now call it */ |
| 104 | rnk = (relocate_new_kernel_t) reboot_code_buffer; | 133 | rnk = (relocate_new_kernel_t) reboot_code_buffer; |
| 105 | (*rnk)(page_list, reboot_code_buffer, P2SEGADDR(image->start), vbr_reg); | 134 | (*rnk)(page_list, reboot_code_buffer, |
| 135 | (unsigned long)phys_to_virt(image->start)); | ||
| 136 | |||
| 137 | #ifdef CONFIG_KEXEC_JUMP | ||
| 138 | asm volatile("ldc %0, vbr" : : "r" (&vbr_base) : "memory"); | ||
| 139 | |||
| 140 | if (image->preserve_context) | ||
| 141 | restore_processor_state(); | ||
| 142 | |||
| 143 | /* Convert page list back to physical addresses, what a mess. */ | ||
| 144 | for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE); | ||
| 145 | ptr = (*ptr & IND_INDIRECTION) ? | ||
| 146 | phys_to_virt(*ptr & PAGE_MASK) : ptr + 1) { | ||
| 147 | if (*ptr & IND_SOURCE || *ptr & IND_INDIRECTION || | ||
| 148 | *ptr & IND_DESTINATION) | ||
| 149 | *ptr = virt_to_phys(*ptr); | ||
| 150 | } | ||
| 151 | #endif | ||
| 152 | |||
| 153 | __ftrace_enabled_restore(save_ftrace_enabled); | ||
| 106 | } | 154 | } |
| 107 | 155 | ||
| 108 | void arch_crash_save_vmcoreinfo(void) | 156 | void arch_crash_save_vmcoreinfo(void) |
