diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index 9ed551b6c172..e7776a438a84 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -65,8 +65,9 @@ pci64-$(CONFIG_PPC64) += pci_64.o pci_dn.o pci_iommu.o \ pci_direct_iommu.o iomap.o obj-$(CONFIG_PCI) += $(pci64-y) -kexec64-$(CONFIG_PPC64) += machine_kexec_64.o -obj-$(CONFIG_KEXEC) += $(kexec64-y) +kexec-$(CONFIG_PPC64) := machine_kexec_64.o +kexec-$(CONFIG_PPC32) := machine_kexec_32.o +obj-$(CONFIG_KEXEC) += machine_kexec.o $(kexec-y) ifeq ($(CONFIG_PPC_ISERIES),y) $(obj)/head_64.o: $(obj)/lparmap.s diff --git a/arch/powerpc/kernel/machine_kexec.c b/arch/powerpc/kernel/machine_kexec.c new file mode 100644 index 000000000000..d8225c797199 --- /dev/null +++ b/arch/powerpc/kernel/machine_kexec.c @@ -0,0 +1,67 @@ +/* + * Code to handle transition of Linux booting another kernel. + * + * Copyright (C) 2002-2003 Eric Biederman + * GameCube/ppc32 port Copyright (C) 2004 Albert Herranz + * Copyright (C) 2005 IBM Corporation. + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include +#include +#include +#include + +/* + * Provide a dummy crash_notes definition until crash dump is implemented. + * This prevents breakage of crash_notes attribute in kernel/ksysfs.c. + */ +note_buf_t crash_notes[NR_CPUS]; + +void machine_crash_shutdown(struct pt_regs *regs) +{ + if (ppc_md.machine_crash_shutdown) + ppc_md.machine_crash_shutdown(); +} + +/* + * Do what every setup is needed on image and the + * reboot code buffer to allow us to avoid allocations + * later. + */ +int machine_kexec_prepare(struct kimage *image) +{ + if (ppc_md.machine_kexec_prepare) + return ppc_md.machine_kexec_prepare(image); + /* + * Fail if platform doesn't provide its own machine_kexec_prepare + * implementation. + */ + return -ENOSYS; +} + +void machine_kexec_cleanup(struct kimage *image) +{ + if (ppc_md.machine_kexec_cleanup) + ppc_md.machine_kexec_cleanup(image); +} + +/* + * 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) +{ + if (ppc_md.machine_kexec) + ppc_md.machine_kexec(image); + else { + /* + * Fall back to normal restart if platform doesn't provide + * its own kexec function, and user insist to kexec... + */ + machine_restart(NULL); + } + for(;;); +} diff --git a/arch/powerpc/kernel/machine_kexec_32.c b/arch/powerpc/kernel/machine_kexec_32.c new file mode 100644 index 000000000000..443606134dff --- /dev/null +++ b/arch/powerpc/kernel/machine_kexec_32.c @@ -0,0 +1,65 @@ +/* + * PPC32 code to handle Linux booting another kernel. + * + * Copyright (C) 2002-2003 Eric Biederman + * GameCube/ppc32 port Copyright (C) 2004 Albert Herranz + * Copyright (C) 2005 IBM Corporation. + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#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; + +/* + * This is a generic machine_kexec function suitable at least for + * non-OpenFirmware embedded platforms. + * It merely copies the image relocation code to the control page and + * jumps to it. + * A platform specific function may just call this one. + */ +void default_machine_kexec(struct kimage *image) +{ + const extern unsigned char relocate_new_kernel[]; + const extern unsigned int relocate_new_kernel_size; + unsigned long page_list; + unsigned long reboot_code_buffer, reboot_code_buffer_phys; + relocate_new_kernel_t rnk; + + /* Interrupts aren't acceptable while we reboot */ + local_irq_disable(); + + page_list = image->head; + + /* we need both effective and real address here */ + reboot_code_buffer = + (unsigned long)page_address(image->control_code_page); + reboot_code_buffer_phys = virt_to_phys((void *)reboot_code_buffer); + + /* copy our kernel relocation code to the control code page */ + memcpy((void *)reboot_code_buffer, relocate_new_kernel, + relocate_new_kernel_size); + + flush_icache_range(reboot_code_buffer, + reboot_code_buffer + KEXEC_CONTROL_CODE_SIZE); + printk(KERN_INFO "Bye!\n"); + + /* now call it */ + rnk = (relocate_new_kernel_t) reboot_code_buffer; + (*rnk)(page_list, reboot_code_buffer_phys, image->start); +} + +int default_machine_kexec_prepare(struct kimage *image) +{ + return 0; +} diff --git a/arch/powerpc/kernel/machine_kexec_64.c b/arch/powerpc/kernel/machine_kexec_64.c index 97c51e452be7..ec0f06bfc24a 100644 --- a/arch/powerpc/kernel/machine_kexec_64.c +++ b/arch/powerpc/kernel/machine_kexec_64.c @@ -1,5 +1,5 @@ /* - * machine_kexec.c - handle transition of Linux booting another kernel + * PPC64 code to handle Linux booting another kernel. * * Copyright (C) 2004-2005, IBM Corp. * @@ -28,21 +28,7 @@ #define HASH_GROUP_SIZE 0x80 /* size of each hash group, asm/mmu.h */ -/* Have this around till we move it into crash specific file */ -note_buf_t crash_notes[NR_CPUS]; - -/* Dummy for now. Not sure if we need to have a crash shutdown in here - * and if what it will achieve. Letting it be now to compile the code - * in generic kexec environment - */ -void machine_crash_shutdown(struct pt_regs *regs) -{ - /* do nothing right now */ - /* smp_relase_cpus() if we want smp on panic kernel */ - /* cpu_irq_down to isolate us until we are ready */ -} - -int machine_kexec_prepare(struct kimage *image) +int default_machine_kexec_prepare(struct kimage *image) { int i; unsigned long begin, end; /* limits of segment */ @@ -111,11 +97,6 @@ int machine_kexec_prepare(struct kimage *image) return 0; } -void machine_kexec_cleanup(struct kimage *image) -{ - /* we do nothing in prepare that needs to be undone */ -} - #define IND_FLAGS (IND_DESTINATION | IND_INDIRECTION | IND_DONE | IND_SOURCE) static void copy_segments(unsigned long ind) @@ -283,7 +264,7 @@ extern NORET_TYPE void kexec_sequence(void *newstack, unsigned long start, void (*clear_all)(void)) ATTRIB_NORET; /* too late to fail here */ -void machine_kexec(struct kimage *image) +void default_machine_kexec(struct kimage *image) { /* prepare control code if any */ diff --git a/arch/powerpc/kernel/misc_32.S b/arch/powerpc/kernel/misc_32.S index 624a983a9676..01d0d97a16e1 100644 --- a/arch/powerpc/kernel/misc_32.S +++ b/arch/powerpc/kernel/misc_32.S @@ -5,6 +5,10 @@ * Largely rewritten by Cort Dougan (cort@cs.nmt.edu) * and Paul Mackerras. * + * kexec bits: + * Copyright (C) 2002-2003 Eric Biederman + * GameCube/ppc32 port Copyright (C) 2004 Albert Herranz + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version @@ -24,6 +28,8 @@ #include #include #include +#include +#include .text @@ -1006,3 +1012,110 @@ _GLOBAL(execve) */ _GLOBAL(__main) blr + +#ifdef CONFIG_KEXEC + /* + * Must be relocatable PIC code callable as a C function. + */ + .globl relocate_new_kernel +relocate_new_kernel: + /* r3 = page_list */ + /* r4 = reboot_code_buffer */ + /* r5 = start_address */ + + li r0, 0 + + /* + * Set Machine Status Register to a known status, + * switch the MMU off and jump to 1: in a single step. + */ + + mr r8, r0 + ori r8, r8, MSR_RI|MSR_ME + mtspr SPRN_SRR1, r8 + addi r8, r4, 1f - relocate_new_kernel + mtspr SPRN_SRR0, r8 + sync + rfi + +1: + /* from this point address translation is turned off */ + /* and interrupts are disabled */ + + /* set a new stack at the bottom of our page... */ + /* (not really needed now) */ + addi r1, r4, KEXEC_CONTROL_CODE_SIZE - 8 /* for LR Save+Back Chain */ + stw r0, 0(r1) + + /* Do the copies */ + li r6, 0 /* checksum */ + mr r0, r3 + b 1f + +0: /* top, read another word for the indirection page */ + lwzu r0, 4(r3) + +1: + /* is it a destination page? (r8) */ + rlwinm. r7, r0, 0, 31, 31 /* IND_DESTINATION (1<<0) */ + beq 2f + + rlwinm r8, r0, 0, 0, 19 /* clear kexec flags, page align */ + b 0b + +2: /* is it an indirection page? (r3) */ + rlwinm. r7, r0, 0, 30, 30 /* IND_INDIRECTION (1<<1) */ + beq 2f + + rlwinm r3, r0, 0, 0, 19 /* clear kexec flags, page align */ + subi r3, r3, 4 + b 0b + +2: /* are we done? */ + rlwinm. r7, r0, 0, 29, 29 /* IND_DONE (1<<2) */ + beq 2f + b 3f + +2: /* is it a source page? (r9) */ + rlwinm. r7, r0, 0, 28, 28 /* IND_SOURCE (1<<3) */ + beq 0b + + rlwinm r9, r0, 0, 0, 19 /* clear kexec flags, page align */ + + li r7, PAGE_SIZE / 4 + mtctr r7 + subi r9, r9, 4 + subi r8, r8, 4 +9: + lwzu r0, 4(r9) /* do the copy */ + xor r6, r6, r0 + stwu r0, 4(r8) + dcbst 0, r8 + sync + icbi 0, r8 + bdnz 9b + + addi r9, r9, 4 + addi r8, r8, 4 + b 0b + +3: + + /* To be certain of avoiding problems with self-modifying code + * execute a serializing instruction here. + */ + isync + sync + + /* jump to the entry point, usually the setup routine */ + mtlr r5 + blrl + +1: b 1b + +relocate_new_kernel_end: + + .globl relocate_new_kernel_size +relocate_new_kernel_size: + .long relocate_new_kernel_end - relocate_new_kernel +#endif diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c index bd3eb4292b53..6088a39edc26 100644 --- a/arch/powerpc/kernel/setup-common.c +++ b/arch/powerpc/kernel/setup-common.c @@ -93,8 +93,8 @@ EXPORT_SYMBOL(ppc_do_canonicalize_irqs); /* also used by kexec */ void machine_shutdown(void) { - if (ppc_md.nvram_sync) - ppc_md.nvram_sync(); + if (ppc_md.machine_shutdown) + ppc_md.machine_shutdown(); } void machine_restart(char *cmd) diff --git a/arch/powerpc/platforms/cell/setup.c b/arch/powerpc/platforms/cell/setup.c index 9a495634d0c2..d45dc18855a5 100644 --- a/arch/powerpc/platforms/cell/setup.c +++ b/arch/powerpc/platforms/cell/setup.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -138,4 +139,8 @@ struct machdep_calls __initdata cell_md = { .set_rtc_time = rtas_set_rtc_time, .calibrate_decr = generic_calibrate_decr, .progress = cell_progress, +#ifdef CONFIG_KEXEC + .machine_kexec = default_machine_kexec, + .machine_kexec_prepare = default_machine_kexec_prepare, +#endif }; diff --git a/arch/powerpc/platforms/maple/setup.c b/arch/powerpc/platforms/maple/setup.c index 7ece8983a105..95b2352655fe 100644 --- a/arch/powerpc/platforms/maple/setup.c +++ b/arch/powerpc/platforms/maple/setup.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -292,4 +293,8 @@ struct machdep_calls __initdata maple_md = { .calibrate_decr = generic_calibrate_decr, .progress = maple_progress, .idle_loop = native_idle, +#ifdef CONFIG_KEXEC + .machine_kexec = default_machine_kexec, + .machine_kexec_prepare = default_machine_kexec_prepare, +#endif }; diff --git a/arch/powerpc/platforms/powermac/nvram.c b/arch/powerpc/platforms/powermac/nvram.c index 4042e2f06ee0..59e0e51cf663 100644 --- a/arch/powerpc/platforms/powermac/nvram.c +++ b/arch/powerpc/platforms/powermac/nvram.c @@ -549,6 +549,7 @@ static int __init core99_nvram_setup(struct device_node *dp) ppc_md.nvram_write = core99_nvram_write; ppc_md.nvram_size = core99_nvram_size; ppc_md.nvram_sync = core99_nvram_sync; + ppc_md.machine_shutdown = core99_nvram_sync; /* * Maybe we could be smarter here though making an exclusive list * of known flash chips is a bit nasty as older OF didn't provide us diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c index 7acb0546671f..3b7a492d9b68 100644 --- a/arch/powerpc/platforms/powermac/setup.c +++ b/arch/powerpc/platforms/powermac/setup.c @@ -60,6 +60,7 @@ #include #include #include +#include #include #include #include @@ -773,7 +774,11 @@ struct machdep_calls __initdata pmac_md = { .pci_probe_mode = pmac_probe_mode, .idle_loop = native_idle, .enable_pmcs = power4_enable_pmcs, +#ifdef CONFIG_KEXEC + .machine_kexec = default_machine_kexec, + .machine_kexec_prepare = default_machine_kexec_prepare, #endif +#endif /* CONFIG_PPC64 */ #ifdef CONFIG_PPC32 .pcibios_enable_device_hook = pmac_pci_enable_device_hook, .pcibios_after_init = pmac_pcibios_after_init, diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 4a465f067ede..8a4238a3757f 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include "xics.h" @@ -638,5 +639,7 @@ struct machdep_calls __initdata pSeries_md = { .machine_check_exception = pSeries_machine_check_exception, #ifdef CONFIG_KEXEC .kexec_cpu_down = pseries_kexec_cpu_down, + .machine_kexec = default_machine_kexec, + .machine_kexec_prepare = default_machine_kexec_prepare, #endif }; diff --git a/arch/ppc/kernel/Makefile b/arch/ppc/kernel/Makefile index 0bb23fce4293..e6c1d615bb86 100644 --- a/arch/ppc/kernel/Makefile +++ b/arch/ppc/kernel/Makefile @@ -49,5 +49,4 @@ obj-$(CONFIG_TAU) += temp.o ifndef CONFIG_E200 obj-$(CONFIG_FSL_BOOKE) += perfmon_fsl_booke.o endif -obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o endif diff --git a/include/asm-powerpc/kexec.h b/include/asm-powerpc/kexec.h index c72ffc709ea8..934b4981651d 100644 --- a/include/asm-powerpc/kexec.h +++ b/include/asm-powerpc/kexec.h @@ -41,10 +41,11 @@ extern note_buf_t crash_notes[]; extern void kexec_smp_wait(void); /* get and clear naca physid, wait for master to copy new code to 0 */ extern void __init kexec_setup(void); -#else -struct kimage; -extern void machine_kexec_simple(struct kimage *image); #endif +struct kimage; +extern void default_machine_kexec(struct kimage *image); +extern int default_machine_kexec_prepare(struct kimage *image); + #endif /* ! __ASSEMBLY__ */ #endif /* _ASM_POWERPC_KEXEC_H */ diff --git a/include/asm-powerpc/machdep.h b/include/asm-powerpc/machdep.h index c011abb8b600..d6a1a2b5507d 100644 --- a/include/asm-powerpc/machdep.h +++ b/include/asm-powerpc/machdep.h @@ -27,6 +27,9 @@ struct device_node; struct iommu_table; struct rtc_time; struct file; +#ifdef CONFIG_KEXEC +struct kimage; +#endif #ifdef CONFIG_SMP struct smp_ops_t { @@ -207,14 +210,14 @@ struct machdep_calls { /* this is for modules, since _machine can be a define -- Cort */ int ppc_machine; +#endif /* CONFIG_PPC32 */ -#ifdef CONFIG_KEXEC /* Called to shutdown machine specific hardware not already controlled * by other drivers. - * XXX Should we move this one out of kexec scope? */ void (*machine_shutdown)(void); +#ifdef CONFIG_KEXEC /* Called to do the minimal shutdown needed to run a kexec'd kernel * to run successfully. * XXX Should we move this one out of kexec scope? @@ -237,7 +240,6 @@ struct machdep_calls { */ void (*machine_kexec)(struct kimage *image); #endif /* CONFIG_KEXEC */ -#endif /* CONFIG_PPC32 */ }; extern void default_idle(void);