[PATCH 1/3] m68k: Add preliminary kexec support

Geert Uytterhoeven geert at linux-m68k.org
Tue Sep 17 06:01:31 EDT 2013


Signed-off-by: Geert Uytterhoeven <geert at linux-m68k.org>
---
 arch/m68k/Kconfig                  |   17 ++++++++
 arch/m68k/include/asm/kexec.h      |   29 +++++++++++++
 arch/m68k/kernel/Makefile          |    2 +
 arch/m68k/kernel/machine_kexec.c   |   71 ++++++++++++++++++++++++++++++++
 arch/m68k/kernel/relocate_kernel.S |   79 ++++++++++++++++++++++++++++++++++++
 include/uapi/linux/kexec.h         |    1 +
 6 files changed, 199 insertions(+)
 create mode 100644 arch/m68k/include/asm/kexec.h
 create mode 100644 arch/m68k/kernel/machine_kexec.c
 create mode 100644 arch/m68k/kernel/relocate_kernel.S

diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig
index 311a300..d60497f 100644
--- a/arch/m68k/Kconfig
+++ b/arch/m68k/Kconfig
@@ -86,6 +86,23 @@ config MMU_SUN3
 	bool
 	depends on MMU && !MMU_MOTOROLA && !MMU_COLDFIRE
 
+config KEXEC
+	bool "kexec system call"
+	depends on MMU # FIXME
+	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 independent of the system firmware.   And like a reboot
+	  you can start any kernel with it, not just Linux.
+
+	  The name comes from the similarity 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.  As of this writing the exact hardware
+	  interface is strongly in flux, so no good recommendation can be
+	  made.
+
 menu "Platform setup"
 
 source arch/m68k/Kconfig.cpu
diff --git a/arch/m68k/include/asm/kexec.h b/arch/m68k/include/asm/kexec.h
new file mode 100644
index 0000000..3df97ab
--- /dev/null
+++ b/arch/m68k/include/asm/kexec.h
@@ -0,0 +1,29 @@
+#ifndef _ASM_M68K_KEXEC_H
+#define _ASM_M68K_KEXEC_H
+
+#ifdef CONFIG_KEXEC
+
+/* 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 code buffer */
+#define KEXEC_CONTROL_MEMORY_LIMIT (-1UL)
+
+#define KEXEC_CONTROL_PAGE_SIZE	4096
+
+#define KEXEC_ARCH KEXEC_ARCH_68K
+
+#ifndef __ASSEMBLY__
+
+static inline void crash_setup_regs(struct pt_regs *newregs,
+				    struct pt_regs *oldregs)
+{
+	/* Dummy implementation for now */
+}
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* CONFIG_KEXEC */
+
+#endif /* _ASM_M68K_KEXEC_H */
diff --git a/arch/m68k/kernel/Makefile b/arch/m68k/kernel/Makefile
index 655347d..7ee5f00 100644
--- a/arch/m68k/kernel/Makefile
+++ b/arch/m68k/kernel/Makefile
@@ -22,3 +22,5 @@ obj-$(CONFIG_PCI) += pcibios.o
 
 obj-$(CONFIG_HAS_DMA)	+= dma.o
 
+obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o
+
diff --git a/arch/m68k/kernel/machine_kexec.c b/arch/m68k/kernel/machine_kexec.c
new file mode 100644
index 0000000..c775da3
--- /dev/null
+++ b/arch/m68k/kernel/machine_kexec.c
@@ -0,0 +1,71 @@
+/*
+ * machine_kexec.c - handle transition of Linux booting another kernel
+ */
+#include <linux/compiler.h>
+#include <linux/kexec.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+
+#include <asm/cacheflush.h>
+#include <asm/page.h>
+
+extern const unsigned char relocate_new_kernel[];
+extern const size_t relocate_new_kernel_size;
+
+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)
+{
+}
+
+typedef void (*relocate_kernel_t)(unsigned long ptr,
+				  unsigned long start) __noreturn;
+
+void machine_kexec(struct kimage *image)
+{
+	void *reboot_code_buffer;
+	unsigned long kexec_indirection_page;
+
+	unsigned long entry;
+	unsigned long *ptr;
+
+	reboot_code_buffer = page_address(image->control_code_page);
+
+	memcpy(reboot_code_buffer, relocate_new_kernel,
+	       relocate_new_kernel_size);
+
+	/*
+	 * The generic kexec code builds a page list with physical
+	 * addresses, while we need virtual addresses
+	 */
+	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 = (unsigned long) phys_to_virt(*ptr);
+	}
+
+	kexec_indirection_page = image->head & PAGE_MASK;
+
+	/*
+	 * we do not want to be bothered.
+	 */
+	local_irq_disable();
+
+	pr_info("Will call new kernel at 0x%08lx. Bye...\n", image->start);
+	__flush_cache_all();
+	((relocate_kernel_t) reboot_code_buffer)(kexec_indirection_page,
+						 image->start);
+}
diff --git a/arch/m68k/kernel/relocate_kernel.S b/arch/m68k/kernel/relocate_kernel.S
new file mode 100644
index 0000000..8d45d9c
--- /dev/null
+++ b/arch/m68k/kernel/relocate_kernel.S
@@ -0,0 +1,79 @@
+#include <linux/linkage.h>
+
+#include <asm/page.h>
+
+
+.globl relocate_new_kernel
+
+.text
+ENTRY(relocate_new_kernel)
+	moveq #0,%d0
+	.chip 68040	/* FIXME */
+	/* Disable MMU */
+	/* FIXME Keep caches enabled? */
+	movec %d0,%tc
+	movec %d0,%itt0
+	movec %d0,%itt1
+	movec %d0,%dtt0
+	movec %d0,%dtt1
+	.chip 68k
+
+	movel 4(%sp),%a0		/* a0 = ptr */
+	movel 8(%sp),%a1		/* a1 = start */
+	movew #PAGE_MASK,%d2		/* d2 = PAGE_MASK */
+
+1:
+	movel (%a0)+,%d0		/* d0 = entry = *ptr */
+	jeq 5f
+
+	btst #2,%d0			/* entry & IND_DONE? */
+	jne 5f
+
+	btst #1,%d0			/* entry & IND_INDIRECTION? */
+	jeq 2f
+	andw %d2,%d0
+	movel %d0,%a0			/* ptr = entry & PAGE_MASK */
+	bra 1b
+
+2:
+	btst #0,%d0			/* entry & IND_DESTINATION? */
+	jeq 3f
+	andw %d2,%d0
+	movel %d0,%a2			/* a2 = dst = entry & PAGE_MASK */
+	bra 1b
+
+3:
+	btst #3,%d0			/* entry & IND_SOURCE? */
+	jeq 1b
+
+	andw %d2,%d0
+	movel %d0,%a3			/* a3 = src = entry & PAGE_MASK */
+	movew #PAGE_SIZE/32 - 1,%d0	/* d0 = PAGE_SIZE/32 - 1 */
+4:
+	movel (%a3)+,(%a2)+		/* *dst++ = *src++ */
+	movel (%a3)+,(%a2)+		/* *dst++ = *src++ */
+	movel (%a3)+,(%a2)+		/* *dst++ = *src++ */
+	movel (%a3)+,(%a2)+		/* *dst++ = *src++ */
+	movel (%a3)+,(%a2)+		/* *dst++ = *src++ */
+	movel (%a3)+,(%a2)+		/* *dst++ = *src++ */
+	movel (%a3)+,(%a2)+		/* *dst++ = *src++ */
+	movel (%a3)+,(%a2)+		/* *dst++ = *src++ */
+	dbf %d0, 4b
+	bra 1b
+
+5:
+	.chip 68040	/* FIXME */
+	/* Flush all caches */
+	nop
+	cpusha %bc
+	nop
+	cinva %bc
+	nop
+	.chip 68k
+
+	jmp (%a1)
+
+relocate_new_kernel_end:
+
+ENTRY(relocate_new_kernel_size)
+	.long relocate_new_kernel_end - relocate_new_kernel
diff --git a/include/uapi/linux/kexec.h b/include/uapi/linux/kexec.h
index 104838f..d6629d4 100644
--- a/include/uapi/linux/kexec.h
+++ b/include/uapi/linux/kexec.h
@@ -18,6 +18,7 @@
  */
 #define KEXEC_ARCH_DEFAULT ( 0 << 16)
 #define KEXEC_ARCH_386     ( 3 << 16)
+#define KEXEC_ARCH_68K     ( 4 << 16)
 #define KEXEC_ARCH_X86_64  (62 << 16)
 #define KEXEC_ARCH_PPC     (20 << 16)
 #define KEXEC_ARCH_PPC64   (21 << 16)
-- 
1.7.9.5




More information about the kexec mailing list