parisc: add kexec syscall support
authorSven Schnelle <svens@stackframe.org>
Sun, 8 Sep 2019 09:33:04 +0000 (11:33 +0200)
committerHelge Deller <deller@gmx.de>
Sun, 8 Sep 2019 13:37:04 +0000 (15:37 +0200)
Signed-off-by: Sven Schnelle <svens@stackframe.org>
Signed-off-by: Helge Deller <deller@gmx.de>
arch/parisc/Kconfig
arch/parisc/include/asm/fixmap.h
arch/parisc/include/asm/kexec.h [new file with mode: 0644]
arch/parisc/kernel/Makefile
arch/parisc/kernel/kexec.c [new file with mode: 0644]
arch/parisc/kernel/relocate_kernel.S [new file with mode: 0644]
include/uapi/linux/kexec.h

index ee59171edffe358781e1f8dd60135f4b85d1e86f..548c767f4358b18bb962bde18964ae5b93547cc1 100644 (file)
@@ -346,6 +346,19 @@ config NR_CPUS
        depends on SMP
        default "4"
 
+config KEXEC
+       bool "Kexec system call"
+       select KEXEC_CORE
+       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.
+
+         It is an ongoing process to be certain the hardware in a machine
+         shutdown, so do not be surprised if this code does not
+         initially work for you.
+
 endmenu
 
 
index 288da73d4cc0d8c4fcb7121855673b27119919f5..e480b2c054078eb9a0f72ff809b2aa3363c27a67 100644 (file)
@@ -30,6 +30,7 @@
 enum fixed_addresses {
        /* Support writing RO kernel text via kprobes, jump labels, etc. */
        FIX_TEXT_POKE0,
+       FIX_TEXT_KEXEC,
        FIX_BITMAP_COUNT
 };
 
diff --git a/arch/parisc/include/asm/kexec.h b/arch/parisc/include/asm/kexec.h
new file mode 100644 (file)
index 0000000..a99ea74
--- /dev/null
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_PARISC_KEXEC_H
+#define _ASM_PARISC_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_PARISC
+#define ARCH_HAS_KIMAGE_ARCH
+
+#ifndef __ASSEMBLY__
+
+struct kimage_arch {
+       unsigned long initrd_start;
+       unsigned long initrd_end;
+       unsigned long cmdline;
+};
+
+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_PARISC_KEXEC_H */
index c232266b517cb8f5760d6e2f0f6bf5c5146161ad..487cf88866a8ad029a4fd11c7fa99e0f92775636 100644 (file)
@@ -37,3 +37,4 @@ obj-$(CONFIG_FUNCTION_GRAPH_TRACER)   += ftrace.o
 obj-$(CONFIG_JUMP_LABEL)               += jump_label.o
 obj-$(CONFIG_KGDB)                     += kgdb.o
 obj-$(CONFIG_KPROBES)                  += kprobes.o
+obj-$(CONFIG_KEXEC)                    += kexec.o relocate_kernel.o
diff --git a/arch/parisc/kernel/kexec.c b/arch/parisc/kernel/kexec.c
new file mode 100644 (file)
index 0000000..a92d265
--- /dev/null
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/kernel.h>
+#include <linux/console.h>
+#include <linux/kexec.h>
+#include <linux/delay.h>
+#include <asm/cacheflush.h>
+#include <asm/sections.h>
+
+extern void relocate_new_kernel(unsigned long head,
+                               unsigned long start,
+                               unsigned long phys);
+
+extern const unsigned int relocate_new_kernel_size;
+extern unsigned int kexec_initrd_start_offset;
+extern unsigned int kexec_initrd_end_offset;
+extern unsigned int kexec_cmdline_offset;
+extern unsigned int kexec_free_mem_offset;
+
+static void kexec_show_segment_info(const struct kimage *kimage,
+                                   unsigned long n)
+{
+       pr_debug("    segment[%lu]: %016lx - %016lx, 0x%lx bytes, %lu pages\n",
+                       n,
+                       kimage->segment[n].mem,
+                       kimage->segment[n].mem + kimage->segment[n].memsz,
+                       (unsigned long)kimage->segment[n].memsz,
+                       (unsigned long)kimage->segment[n].memsz /  PAGE_SIZE);
+}
+
+static void kexec_image_info(const struct kimage *kimage)
+{
+       unsigned long i;
+
+       pr_debug("kexec kimage info:\n");
+       pr_debug("  type:        %d\n", kimage->type);
+       pr_debug("  start:       %lx\n", kimage->start);
+       pr_debug("  head:        %lx\n", kimage->head);
+       pr_debug("  nr_segments: %lu\n", kimage->nr_segments);
+
+       for (i = 0; i < kimage->nr_segments; i++)
+               kexec_show_segment_info(kimage, i);
+}
+
+void machine_kexec_cleanup(struct kimage *kimage)
+{
+}
+
+void machine_crash_shutdown(struct pt_regs *regs)
+{
+}
+
+void machine_shutdown(void)
+{
+       smp_send_stop();
+       while (num_online_cpus() > 1) {
+               cpu_relax();
+               mdelay(1);
+       }
+}
+
+void machine_kexec(struct kimage *image)
+{
+#ifdef CONFIG_64BIT
+       Elf64_Fdesc desc;
+#endif
+       void (*reloc)(unsigned long head,
+                     unsigned long start,
+                     unsigned long phys);
+
+       unsigned long phys = page_to_phys(image->control_code_page);
+       void *virt = (void *)__fix_to_virt(FIX_TEXT_KEXEC);
+       struct kimage_arch *arch = &image->arch;
+
+       set_fixmap(FIX_TEXT_KEXEC, phys);
+
+       flush_cache_all();
+
+#ifdef CONFIG_64BIT
+       reloc = (void *)&desc;
+       desc.addr = (long long)virt;
+#else
+       reloc = (void *)virt;
+#endif
+
+       memcpy(virt, dereference_function_descriptor(relocate_new_kernel),
+               relocate_new_kernel_size);
+
+       *(unsigned long *)(virt + kexec_cmdline_offset) = arch->cmdline;
+       *(unsigned long *)(virt + kexec_initrd_start_offset) = arch->initrd_start;
+       *(unsigned long *)(virt + kexec_initrd_end_offset) = arch->initrd_end;
+       *(unsigned long *)(virt + kexec_free_mem_offset) = PAGE0->mem_free;
+
+       flush_cache_all();
+       flush_tlb_all();
+       local_irq_disable();
+
+       reloc(image->head & PAGE_MASK, image->start, phys);
+}
+
+int machine_kexec_prepare(struct kimage *image)
+{
+       kexec_image_info(image);
+       return 0;
+}
diff --git a/arch/parisc/kernel/relocate_kernel.S b/arch/parisc/kernel/relocate_kernel.S
new file mode 100644 (file)
index 0000000..2561e52
--- /dev/null
@@ -0,0 +1,149 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#include <linux/linkage.h>
+#include <linux/kexec.h>
+
+#include <asm/assembly.h>
+#include <asm/asm-offsets.h>
+#include <asm/page.h>
+#include <asm/setup.h>
+#include <asm/psw.h>
+
+.level PA_ASM_LEVEL
+
+.macro kexec_param name
+.align 8
+ENTRY(kexec\()_\name)
+#ifdef CONFIG_64BIT
+       .dword 0
+#else
+       .word 0
+#endif
+
+ENTRY(kexec\()_\name\()_offset)
+       .word kexec\()_\name - relocate_new_kernel
+.endm
+
+.text
+
+/* args:
+ * r26 - kimage->head
+ * r25 - start address of kernel
+ * r24 - physical address of relocate code
+ */
+
+ENTRY_CFI(relocate_new_kernel)
+0:     copy    %arg1, %rp
+       /* disable I and Q bit, so we are allowed to execute RFI */
+       rsm PSW_SM_I, %r0
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+
+       rsm PSW_SM_Q, %r0
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+
+       /*
+        * After return-from-interrupt, we want to run without Code/Data
+        * translation enabled just like on a normal boot.
+        */
+
+       /* calculate new physical execution address */
+       ldo     1f-0b(%arg2), %r1
+       mtctl   %r0, %cr17 /* IIASQ */
+       mtctl   %r0, %cr17 /* IIASQ */
+       mtctl   %r1, %cr18 /* IIAOQ */
+       ldo     4(%r1),%r1
+       mtctl   %r1, %cr18 /* IIAOQ */
+#ifdef CONFIG_64BIT
+       depdi,z 1, PSW_W_BIT, 1, %r1
+       mtctl   %r1, %cr22 /* IPSW */
+#else
+       mtctl   %r0, %cr22 /* IPSW */
+#endif
+       /* lets go... */
+       rfi
+1:     nop
+       nop
+
+.Lloop:
+       LDREG,ma        REG_SZ(%arg0), %r3
+       /* If crash kernel, no copy needed */
+       cmpib,COND(=),n 0,%r3,boot
+
+       bb,<,n          %r3, 31 - IND_DONE_BIT, boot
+       bb,>=,n         %r3, 31 - IND_INDIRECTION_BIT, .Lnotind
+       /* indirection, load and restart */
+       movb            %r3, %arg0, .Lloop
+       depi            0, 31, PAGE_SHIFT, %arg0
+
+.Lnotind:
+       bb,>=,n         %r3, 31 - IND_DESTINATION_BIT, .Lnotdest
+       b               .Lloop
+       copy            %r3, %r20
+
+.Lnotdest:
+       bb,>=           %r3, 31 - IND_SOURCE_BIT, .Lloop
+       depi            0, 31, PAGE_SHIFT, %r3
+       copy            %r3, %r21
+
+       /* copy page */
+       copy            %r0, %r18
+       zdepi           1, 31 - PAGE_SHIFT, 1, %r18
+       add             %r20, %r18, %r17
+
+       depi            0, 31, PAGE_SHIFT, %r20
+.Lcopy:
+       copy            %r20, %r12
+       LDREG,ma        REG_SZ(%r21), %r8
+       LDREG,ma        REG_SZ(%r21), %r9
+       LDREG,ma        REG_SZ(%r21), %r10
+       LDREG,ma        REG_SZ(%r21), %r11
+       STREG,ma        %r8, REG_SZ(%r20)
+       STREG,ma        %r9, REG_SZ(%r20)
+       STREG,ma        %r10, REG_SZ(%r20)
+       STREG,ma        %r11, REG_SZ(%r20)
+
+#ifndef CONFIG_64BIT
+       LDREG,ma        REG_SZ(%r21), %r8
+       LDREG,ma        REG_SZ(%r21), %r9
+       LDREG,ma        REG_SZ(%r21), %r10
+       LDREG,ma        REG_SZ(%r21), %r11
+       STREG,ma        %r8, REG_SZ(%r20)
+       STREG,ma        %r9, REG_SZ(%r20)
+       STREG,ma        %r10, REG_SZ(%r20)
+       STREG,ma        %r11, REG_SZ(%r20)
+#endif
+
+       fdc             %r0(%r12)
+       cmpb,COND(<<)   %r20,%r17,.Lcopy
+       fic             (%sr4, %r12)
+       b,n             .Lloop
+
+boot:
+       mtctl   %r0, %cr15
+
+       LDREG   kexec_free_mem-0b(%arg2), %arg0
+       LDREG   kexec_cmdline-0b(%arg2), %arg1
+       LDREG   kexec_initrd_end-0b(%arg2), %arg3
+       LDREG   kexec_initrd_start-0b(%arg2), %arg2
+       bv,n %r0(%rp)
+
+ENDPROC_CFI(relocate_new_kernel);
+
+ENTRY(relocate_new_kernel_size)
+       .word relocate_new_kernel_size - relocate_new_kernel
+
+kexec_param cmdline
+kexec_param initrd_start
+kexec_param initrd_end
+kexec_param free_mem
index 6d112868272da25419a1feda5a43ab04e8ad15d9..05669c87a0afb93a4863186f3789d730b4c2c18e 100644 (file)
@@ -31,6 +31,7 @@
 #define KEXEC_ARCH_DEFAULT ( 0 << 16)
 #define KEXEC_ARCH_386     ( 3 << 16)
 #define KEXEC_ARCH_68K     ( 4 << 16)
+#define KEXEC_ARCH_PARISC  (15 << 16)
 #define KEXEC_ARCH_X86_64  (62 << 16)
 #define KEXEC_ARCH_PPC     (20 << 16)
 #define KEXEC_ARCH_PPC64   (21 << 16)