diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 32696c1d92806c7cac9eebaa20220aacd6793993..6600ee87f896938bd7e71f30bc4e43c69e148c2a 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -455,6 +455,14 @@ config NO_IDLE_HZ_INIT The HZ timer is switched off in idle by default. That means the HZ timer is already disabled at boot time. +config KEXEC + bool "kexec system call (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + kexec is a system call that implements the ability to shutdown your + current kernel, and to start another kernel. It is like a reboot + but is independent of hardware/microcode support. + endmenu config PCMCIA diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index b41e0e199a7cf8aa9d5b8f221b72b4f58b27b4bc..ab1e49d2e5185dad2116aa8ff8afacf5e6f2cf2e 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -25,6 +25,16 @@ obj-$(CONFIG_ARCH_S390X) += entry64.o reipl64.o obj-$(CONFIG_VIRT_TIMER) += vtime.o +# Kexec part +S390_KEXEC_OBJS := machine_kexec.o crash.o +ifeq ($(CONFIG_ARCH_S390X),y) +S390_KEXEC_OBJS += relocate_kernel64.o +else +S390_KEXEC_OBJS += relocate_kernel.o +endif +obj-$(CONFIG_KEXEC) += $(S390_KEXEC_OBJS) + + # # This is just to get the dependencies... # diff --git a/arch/s390/kernel/compat_wrapper.S b/arch/s390/kernel/compat_wrapper.S index 7a607b1d03808f6abdb36e47de42d6c5ef91e2d8..bf529739c8ab6a53709b4bf5507ae7c4ca7410f0 100644 --- a/arch/s390/kernel/compat_wrapper.S +++ b/arch/s390/kernel/compat_wrapper.S @@ -1441,3 +1441,11 @@ compat_sys_waitid_wrapper: lgfr %r5,%r5 # int llgtr %r6,%r6 # struct rusage_emu31 * jg compat_sys_waitid + + .globl compat_sys_kexec_load_wrapper +compat_sys_kexec_load_wrapper: + llgfr %r2,%r2 # unsigned long + llgfr %r3,%r3 # unsigned long + llgtr %r4,%r4 # struct kexec_segment * + llgfr %r5,%r5 # unsigned long + jg compat_sys_kexec_load diff --git a/arch/s390/kernel/crash.c b/arch/s390/kernel/crash.c new file mode 100644 index 0000000000000000000000000000000000000000..db38283c1f276a3bd400e6fb9f52413783d672ca --- /dev/null +++ b/arch/s390/kernel/crash.c @@ -0,0 +1,17 @@ +/* + * arch/s390/kernel/crash.c + * + * (C) Copyright IBM Corp. 2005 + * + * Author(s): Heiko Carstens + * + */ + +#include +#include + +note_buf_t crash_notes[NR_CPUS]; + +void machine_crash_shutdown(void) +{ +} diff --git a/arch/s390/kernel/machine_kexec.c b/arch/s390/kernel/machine_kexec.c new file mode 100644 index 0000000000000000000000000000000000000000..7a94db76df464510f006afd64d43506458f675e1 --- /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 + * + */ + +/* + * s390_machine_kexec.c - handle the transition of Linux booting another kernel + * on the S390 architecture. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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)<control_code_page) << PAGE_SHIFT); + + /* Call the moving routine */ + (*data_mover) (&image->head, image->start); +} diff --git a/arch/s390/kernel/relocate_kernel.S b/arch/s390/kernel/relocate_kernel.S new file mode 100644 index 0000000000000000000000000000000000000000..d5e4a62fbb7965e1e000c28407fbdf388f2dcd0a --- /dev/null +++ b/arch/s390/kernel/relocate_kernel.S @@ -0,0 +1,81 @@ +/* + * arch/s390/kernel/relocate_kernel.S + * + * (C) Copyright IBM Corp. 2005 + * + * Author(s): Rolf Adelsberger + * + */ + +/* + * moves the new kernel to its destination... + * %r2 = pointer to first kimage_entry_t + * %r3 = start address - where to jump to after the job is done... + * + * %r5 will be used as temp. storage + * %r6 holds the destination address + * %r7 = PAGE_SIZE + * %r8 holds the source address + * %r9 = PAGE_SIZE + * %r10 is a page mask + */ + + .text + .globl relocate_kernel + relocate_kernel: + basr %r13,0 #base address + .base: + spx zero64-.base(%r13) #absolute addressing mode + stnsm sys_msk-.base(%r13),0xf8 #disable DAT and IRQ (external) + lhi %r10,-1 #preparing the mask + sll %r10,12 #shift it such that it becomes 0xf000 + .top: + lhi %r7,4096 #load PAGE_SIZE in r7 + lhi %r9,4096 #load PAGE_SIZE in r9 + l %r5,0(%r2) #read another word for indirection page + ahi %r2,4 #increment pointer + tml %r5,0x1 #is it a destination page? + je .indir_check #NO, goto "indir_check" + lr %r6,%r5 #r6 = r5 + nr %r6,%r10 #mask it out and... + j .top #...next iteration + .indir_check: + tml %r5,0x2 #is it a indirection page? + je .done_test #NO, goto "done_test" + nr %r5,%r10 #YES, mask out, + lr %r2,%r5 #move it into the right register, + j .top #and read next... + .done_test: + tml %r5,0x4 #is it the done indicator? + je .source_test #NO! Well, then it should be the source indicator... + j .done #ok, lets finish it here... + .source_test: + tml %r5,0x8 #it should be a source indicator... + je .top #NO, ignore it... + lr %r8,%r5 #r8 = r5 + nr %r8,%r10 #masking + 0: mvcle %r6,%r8,0x0 #copy PAGE_SIZE bytes from r8 to r6 - pad with 0 + jo 0b + j .top + .done: + sr %r0,%r0 #clear register r0 + la %r4,load_psw-.base(%r13) #load psw-address into the register + o %r3,4(%r4) #or load address into psw + st %r3,4(%r4) + mvc 0(8,%r0),0(%r4) #copy psw to absolute address 0 + sr %r1,%r1 #clear %r1 + sr %r2,%r2 #clear %r2 + sigp %r1,%r2,0x12 #set cpuid to zero + lpsw 0 #hopefully start new kernel... + + .align 8 + zero64: + .quad 0 + load_psw: + .long 0x00080000,0x80000000 + sys_msk: + .quad 0 + relocate_kernel_end: + .globl relocate_kernel_len + relocate_kernel_len: + .quad relocate_kernel_end - relocate_kernel diff --git a/arch/s390/kernel/relocate_kernel64.S b/arch/s390/kernel/relocate_kernel64.S new file mode 100644 index 0000000000000000000000000000000000000000..96290cc4eb3c49de81c4c19456db70c2c41dc89d --- /dev/null +++ b/arch/s390/kernel/relocate_kernel64.S @@ -0,0 +1,82 @@ +/* + * arch/s390/kernel/relocate_kernel64.S + * + * (C) Copyright IBM Corp. 2005 + * + * Author(s): Rolf Adelsberger + * + */ + +/* + * moves the new kernel to its destination... + * %r2 = pointer to first kimage_entry_t + * %r3 = start address - where to jump to after the job is done... + * + * %r5 will be used as temp. storage + * %r6 holds the destination address + * %r7 = PAGE_SIZE + * %r8 holds the source address + * %r9 = PAGE_SIZE + * + * 0xf000 is a page_mask + */ + + .text + .globl relocate_kernel + relocate_kernel: + basr %r13,0 #base address + .base: + spx zero64-.base(%r13) #absolute addressing mode + stnsm sys_msk-.base(%r13),0xf8 #disable DAT and IRQ (external) + .top: + lghi %r7,4096 #load PAGE_SIZE in r7 + lghi %r9,4096 #load PAGE_SIZE in r9 + lg %r5,0(%r2) #read another word for indirection page + aghi %r2,8 #increment pointer + tml %r5,0x1 #is it a destination page? + je .indir_check #NO, goto "indir_check" + lgr %r6,%r5 #r6 = r5 + nill %r6,0xf000 #mask it out and... + j .top #...next iteration + .indir_check: + tml %r5,0x2 #is it a indirection page? + je .done_test #NO, goto "done_test" + nill %r5,0xf000 #YES, mask out, + lgr %r2,%r5 #move it into the right register, + j .top #and read next... + .done_test: + tml %r5,0x4 #is it the done indicator? + je .source_test #NO! Well, then it should be the source indicator... + j .done #ok, lets finish it here... + .source_test: + tml %r5,0x8 #it should be a source indicator... + je .top #NO, ignore it... + lgr %r8,%r5 #r8 = r5 + nill %r8,0xf000 #masking + 0: mvcle %r6,%r8,0x0 #copy PAGE_SIZE bytes from r8 to r6 - pad with 0 + jo 0b + j .top + .done: + sgr %r0,%r0 #clear register r0 + la %r4,load_psw-.base(%r13) #load psw-address into the register + o %r3,4(%r4) #or load address into psw + st %r3,4(%r4) + mvc 0(8,%r0),0(%r4) #copy psw to absolute address 0 + sam31 #31 bit mode + sr %r1,%r1 #erase register r1 + sr %r2,%r2 #erase register r2 + sigp %r1,%r2,0x12 #set cpuid to zero + lpsw 0 #hopefully start new kernel... + + .align 8 + zero64: + .quad 0 + load_psw: + .long 0x00080000,0x80000000 + sys_msk: + .quad 0 + relocate_kernel_end: + .globl relocate_kernel_len + relocate_kernel_len: + .quad relocate_kernel_end - relocate_kernel + diff --git a/arch/s390/kernel/syscalls.S b/arch/s390/kernel/syscalls.S index 515938628f82c75a7d80367568e933cfc4e0783c..a8668afb5f87d2b5b3324a8dae1802eab7aedcdf 100644 --- a/arch/s390/kernel/syscalls.S +++ b/arch/s390/kernel/syscalls.S @@ -285,7 +285,7 @@ SYSCALL(sys_mq_timedsend,sys_mq_timedsend,compat_sys_mq_timedsend_wrapper) SYSCALL(sys_mq_timedreceive,sys_mq_timedreceive,compat_sys_mq_timedreceive_wrapper) SYSCALL(sys_mq_notify,sys_mq_notify,compat_sys_mq_notify_wrapper) /* 275 */ SYSCALL(sys_mq_getsetattr,sys_mq_getsetattr,compat_sys_mq_getsetattr_wrapper) -NI_SYSCALL /* reserved for kexec */ +SYSCALL(sys_kexec_load,sys_kexec_load,compat_sys_kexec_load_wrapper) SYSCALL(sys_add_key,sys_add_key,compat_sys_add_key_wrapper) SYSCALL(sys_request_key,sys_request_key,compat_sys_request_key_wrapper) SYSCALL(sys_keyctl,sys_keyctl,compat_sys_keyctl) /* 280 */ diff --git a/include/asm-s390/kexec.h b/include/asm-s390/kexec.h new file mode 100644 index 0000000000000000000000000000000000000000..54cf7d9f251c69f78e171f3f81068ce852fc8210 --- /dev/null +++ b/include/asm-s390/kexec.h @@ -0,0 +1,42 @@ +/* + * include/asm-s390/kexec.h + * + * (C) Copyright IBM Corp. 2005 + * + * Author(s): Rolf Adelsberger + * + */ + +#ifndef _S390_KEXEC_H +#define _S390_KEXEC_H + +#include +#include +/* + * KEXEC_SOURCE_MEMORY_LIMIT maximum page get_free_page can return. + * I.e. Maximum page that is mapped directly into kernel memory, + * and kmap is not required. + */ + +/* Maximum physical address we can use pages from */ +#define KEXEC_SOURCE_MEMORY_LIMIT (-1UL) + +/* Maximum address we can reach in physical address mode */ +#define KEXEC_DESTINATION_MEMORY_LIMIT (-1UL) + +/* Maximum address we can use for the control pages */ +/* Not more than 2GB */ +#define KEXEC_CONTROL_MEMORY_LIMIT (1<<31) + +/* Allocate one page for the pdp and the second for the code */ +#define KEXEC_CONTROL_CODE_SIZE 4096 + +/* The native architecture */ +#define KEXEC_ARCH KEXEC_ARCH_S390 + +#define MAX_NOTE_BYTES 1024 +typedef u32 note_buf_t[MAX_NOTE_BYTES/4]; + +extern note_buf_t crash_notes[]; + +#endif /*_S390_KEXEC_H */ diff --git a/include/asm-s390/unistd.h b/include/asm-s390/unistd.h index f1a204f7c0f0b2559636a815d813766f89d71d1f..363db45f8d074314ed9603bc8c26466aeb270134 100644 --- a/include/asm-s390/unistd.h +++ b/include/asm-s390/unistd.h @@ -269,7 +269,7 @@ #define __NR_mq_timedreceive 274 #define __NR_mq_notify 275 #define __NR_mq_getsetattr 276 -/* Number 277 is reserved for new sys_kexec_load */ +#define __NR_kexec_load 277 #define __NR_add_key 278 #define __NR_request_key 279 #define __NR_keyctl 280 diff --git a/include/linux/kexec.h b/include/linux/kexec.h index e3fc35f4e35fdac44e3132ca93e4d5865def31c5..0653a27c3d724e83f3169ddb5f5a59c6545c8e55 100644 --- a/include/linux/kexec.h +++ b/include/linux/kexec.h @@ -114,6 +114,7 @@ extern struct kimage *kexec_image; #define KEXEC_ARCH_PPC (20 << 16) #define KEXEC_ARCH_PPC64 (21 << 16) #define KEXEC_ARCH_IA_64 (50 << 16) +#define KEXEC_ARCH_S390 (22 << 16) #define KEXEC_FLAGS (KEXEC_ON_CRASH) /* List of defined/legal kexec flags */