diff options
Diffstat (limited to 'arch/s390/kernel/machine_kexec.c')
-rw-r--r-- | arch/s390/kernel/machine_kexec.c | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/arch/s390/kernel/machine_kexec.c b/arch/s390/kernel/machine_kexec.c new file mode 100644 index 000000000000..7a94db76df46 --- /dev/null +++ b/arch/s390/kernel/machine_kexec.c @@ -0,0 +1,98 @@ +/* + * arch/s390/kernel/machine_kexec.c + * + * (C) Copyright IBM Corp. 2005 + * + * Author(s): Rolf Adelsberger <adelsberger@de.ibm.com> + * + */ + +/* + * s390_machine_kexec.c - handle the transition of Linux booting another kernel + * on the S390 architecture. + */ + +#include <asm/cio.h> +#include <asm/setup.h> +#include <linux/device.h> +#include <linux/mm.h> +#include <linux/kexec.h> +#include <linux/delay.h> +#include <asm/pgtable.h> +#include <asm/pgalloc.h> +#include <asm/system.h> + +static void kexec_halt_all_cpus(void *); + +typedef void (*relocate_kernel_t) (kimage_entry_t *, unsigned long); + +const extern unsigned char relocate_kernel[]; +const extern unsigned long long relocate_kernel_len; + +int +machine_kexec_prepare(struct kimage *image) +{ + unsigned long reboot_code_buffer; + + /* We don't support anything but the default image type for now. */ + if (image->type != KEXEC_TYPE_DEFAULT) + return -EINVAL; + + /* Get the destination where the assembler code should be copied to.*/ + reboot_code_buffer = page_to_pfn(image->control_code_page)<<PAGE_SHIFT; + + /* Then copy it */ + memcpy((void *) reboot_code_buffer, relocate_kernel, + relocate_kernel_len); + return 0; +} + +void +machine_kexec_cleanup(struct kimage *image) +{ +} + +void +machine_shutdown(void) +{ + printk(KERN_INFO "kexec: machine_shutdown called\n"); +} + +NORET_TYPE void +machine_kexec(struct kimage *image) +{ + clear_all_subchannels(); + + /* Disable lowcore protection */ + ctl_clear_bit(0,28); + + on_each_cpu(kexec_halt_all_cpus, image, 0, 0); + for(;;); +} + +static void +kexec_halt_all_cpus(void *kernel_image) +{ + static atomic_t cpuid = ATOMIC_INIT(-1); + int cpu; + struct kimage *image; + relocate_kernel_t data_mover; + + if (atomic_compare_and_swap(-1, smp_processor_id(), &cpuid)) + signal_processor(smp_processor_id(), sigp_stop); + + /* Wait for all other cpus to enter stopped state */ + for_each_online_cpu(cpu) { + if (cpu == smp_processor_id()) + continue; + while(!smp_cpu_not_running(cpu)) + cpu_relax(); + } + + image = (struct kimage *) kernel_image; + data_mover = (relocate_kernel_t) + (page_to_pfn(image->control_code_page) << PAGE_SHIFT); + + /* Call the moving routine */ + (*data_mover) (&image->head, image->start); +} |