summary refs log tree commit diff
path: root/arch
diff options
context:
space:
mode:
Diffstat (limited to 'arch')
-rw-r--r--arch/mips/Kconfig17
-rw-r--r--arch/mips/kernel/Makefile2
-rw-r--r--arch/mips/kernel/machine_kexec.c85
-rw-r--r--arch/mips/kernel/relocate_kernel.S80
-rw-r--r--arch/mips/kernel/scall32-o32.S2
-rw-r--r--arch/mips/kernel/scall64-64.S2
-rw-r--r--arch/mips/kernel/scall64-n32.S2
-rw-r--r--arch/mips/kernel/scall64-o32.S2
8 files changed, 188 insertions, 4 deletions
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 1443024b1c7c..c0da0ffe8d57 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -766,6 +766,23 @@ config TOSHIBA_RBTX4938
 
 endchoice
 
+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 it is indepedent of the system firmware.   And like a reboot
+ 	  you can start any kernel with it, not just Linux.
+
+ 	  The name comes from the similiarity to the exec system call.
+
+ 	  It is an ongoing process to be certain the hardware in a machine
+ 	  is properly shutdown, so do not be surprised if this code does not
+ 	  initially work for you.  It may help to enable device hotplugging
+ 	  support.  As of this writing the exact hardware interface is
+ 	  strongly in flux, so no good recommendation can be made.
+
 source "arch/mips/ddb5xxx/Kconfig"
 source "arch/mips/gt64120/ev64120/Kconfig"
 source "arch/mips/jazz/Kconfig"
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile
index 6bfbbed0897e..f35b739d0a12 100644
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -67,6 +67,8 @@ obj-$(CONFIG_64BIT)		+= cpu-bugs64.o
 
 obj-$(CONFIG_I8253)		+= i8253.o
 
+obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o
+
 CFLAGS_cpu-bugs64.o	= $(shell if $(CC) $(CFLAGS) -Wa,-mdaddi -c -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-DHAVE_AS_SET_DADDI"; fi)
 
 EXTRA_AFLAGS := $(CFLAGS)
diff --git a/arch/mips/kernel/machine_kexec.c b/arch/mips/kernel/machine_kexec.c
new file mode 100644
index 000000000000..e0ad754c7edd
--- /dev/null
+++ b/arch/mips/kernel/machine_kexec.c
@@ -0,0 +1,85 @@
+/*
+ * machine_kexec.c for kexec
+ * Created by <nschichan@corp.free.fr> on Thu Oct 12 15:15:06 2006
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2.  See the file COPYING for more details.
+ */
+
+#include <linux/kexec.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+
+#include <asm/cacheflush.h>
+#include <asm/page.h>
+
+const extern unsigned char relocate_new_kernel[];
+const extern unsigned int relocate_new_kernel_size;
+
+extern unsigned long kexec_start_address;
+extern unsigned long kexec_indirection_page;
+
+int
+machine_kexec_prepare(struct kimage *kimage)
+{
+	return 0;
+}
+
+void
+machine_kexec_cleanup(struct kimage *kimage)
+{
+}
+
+void
+machine_shutdown(void)
+{
+}
+
+void
+machine_crash_shutdown(struct pt_regs *regs)
+{
+}
+
+void
+machine_kexec(struct kimage *image)
+{
+	unsigned long reboot_code_buffer;
+	unsigned long entry;
+	unsigned long *ptr;
+
+	reboot_code_buffer =
+	  (unsigned long)page_address(image->control_code_page);
+
+	kexec_start_address = image->start;
+	kexec_indirection_page = phys_to_virt(image->head & PAGE_MASK);
+
+	memcpy((void*)reboot_code_buffer, relocate_new_kernel,
+	       relocate_new_kernel_size);
+
+	/*
+	 * The generic kexec code builds a page list with physical
+	 * addresses. they are directly accessible through KSEG0 (or
+	 * CKSEG0 or XPHYS if on 64bit system), hence the
+	 * pys_to_virt() call.
+	 */
+	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 = phys_to_virt(*ptr);
+	}
+
+	/*
+	 * we do not want to be bothered.
+	 */
+	local_irq_disable();
+
+	flush_icache_range(reboot_code_buffer,
+			   reboot_code_buffer + KEXEC_CONTROL_CODE_SIZE);
+
+	printk("Will call new kernel at %08x\n", image->start);
+	printk("Bye ...\n");
+	flush_cache_all();
+	((void (*)(void))reboot_code_buffer)();
+}
diff --git a/arch/mips/kernel/relocate_kernel.S b/arch/mips/kernel/relocate_kernel.S
new file mode 100644
index 000000000000..a3f0d00c1334
--- /dev/null
+++ b/arch/mips/kernel/relocate_kernel.S
@@ -0,0 +1,80 @@
+/*
+ * relocate_kernel.S for kexec
+ * Created by <nschichan@corp.free.fr> on Thu Oct 12 17:49:57 2006
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2.  See the file COPYING for more details.
+ */
+
+#include <asm/asm.h>
+#include <asm/asmmacro.h>
+#include <asm/regdef.h>
+#include <asm/page.h>
+#include <asm/mipsregs.h>
+#include <asm/stackframe.h>
+#include <asm/addrspace.h>
+
+	.globl relocate_new_kernel
+relocate_new_kernel:
+
+	PTR_L	s0, kexec_indirection_page
+	PTR_L	s1, kexec_start_address
+
+process_entry:
+	PTR_L	s2, (s0)
+	PTR_ADD	s0, s0, SZREG
+
+	/* destination page */
+	and	s3, s2, 0x1
+	beq	s3, zero, 1f
+	and	s4, s2, ~0x1	/* store destination addr in s4 */
+	move	a0, s4
+	b	process_entry
+
+1:
+	/* indirection page, update s0  */
+	and	s3, s2, 0x2
+	beq	s3, zero, 1f
+	and	s0, s2, ~0x2
+	b	process_entry
+
+1:
+	/* done page */
+	and	s3, s2, 0x4
+	beq	s3, zero, 1f
+	b	done
+1:
+	/* source page */
+	and	s3, s2, 0x8
+	beq	s3, zero, process_entry
+	and	s2, s2, ~0x8
+	li	s6, (1 << PAGE_SHIFT) / SZREG
+
+copy_word:
+	/* copy page word by word */
+	REG_L	s5, (s2)
+	REG_S	s5, (s4)
+	INT_ADD	s4, s4, SZREG
+	INT_ADD	s2, s2, SZREG
+	INT_SUB	s6, s6, 1
+	beq	s6, zero, process_entry
+	b	copy_word
+	b	process_entry
+
+done:
+	/* jump to kexec_start_address */
+	j	s1
+
+	.globl kexec_start_address
+kexec_start_address:
+	.long	0x0
+
+	.globl kexec_indirection_page
+kexec_indirection_page:
+	.long	0x0
+
+relocate_new_kernel_end:
+
+	.globl relocate_new_kernel_size
+relocate_new_kernel_size:
+	.long relocate_new_kernel_end - relocate_new_kernel
diff --git a/arch/mips/kernel/scall32-o32.S b/arch/mips/kernel/scall32-o32.S
index a95f37de080e..7c0b3936ba44 100644
--- a/arch/mips/kernel/scall32-o32.S
+++ b/arch/mips/kernel/scall32-o32.S
@@ -653,7 +653,7 @@ einval:	li	v0, -EINVAL
 	sys	sys_move_pages		6
 	sys	sys_set_robust_list	2
 	sys	sys_get_robust_list	3	/* 4310 */
-	sys	sys_ni_syscall		0
+	sys	sys_kexec_load		4
 	sys	sys_getcpu		3
 	sys	sys_epoll_pwait		6
 	.endm
diff --git a/arch/mips/kernel/scall64-64.S b/arch/mips/kernel/scall64-64.S
index 8fb0f60f657b..e569b846e9a3 100644
--- a/arch/mips/kernel/scall64-64.S
+++ b/arch/mips/kernel/scall64-64.S
@@ -468,6 +468,6 @@ sys_call_table:
 	PTR	sys_move_pages
 	PTR	sys_set_robust_list
 	PTR	sys_get_robust_list
-	PTR	sys_ni_syscall			/* 5270 */
+	PTR	sys_kexec_load			/* 5270 */
 	PTR	sys_getcpu
 	PTR	sys_epoll_pwait
diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S
index 0da5ca2040ff..5b18f265d75b 100644
--- a/arch/mips/kernel/scall64-n32.S
+++ b/arch/mips/kernel/scall64-n32.S
@@ -394,6 +394,6 @@ EXPORT(sysn32_call_table)
 	PTR	sys_move_pages
 	PTR	compat_sys_set_robust_list
 	PTR	compat_sys_get_robust_list
-	PTR	sys_ni_syscall
+	PTR	compat_sys_kexec_load
 	PTR	sys_getcpu
 	PTR	sys_epoll_pwait
diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S
index b9d00cae8b5f..e91379c1be1d 100644
--- a/arch/mips/kernel/scall64-o32.S
+++ b/arch/mips/kernel/scall64-o32.S
@@ -516,7 +516,7 @@ sys_call_table:
 	PTR	compat_sys_move_pages
 	PTR	compat_sys_set_robust_list
 	PTR	compat_sys_get_robust_list	/* 4310 */
-	PTR	sys_ni_syscall
+	PTR	compat_sys_kexec_load
 	PTR	sys_getcpu
 	PTR	sys_epoll_pwait
 	.size	sys_call_table,.-sys_call_table