aboutsummaryrefslogtreecommitdiffstats
path: root/arch/parisc
diff options
context:
space:
mode:
authorSven Schnelle <svens@stackframe.org>2019-09-08 05:33:04 -0400
committerHelge Deller <deller@gmx.de>2019-09-08 09:37:04 -0400
commitfc697dc0c26a5908d467454e49440862d7fe96d0 (patch)
tree89a0cc633b00b294644143b4a11a78bf18c77e0d /arch/parisc
parent507efd63d98c4437d62bbfa932b322e72723e1fc (diff)
parisc: add kexec syscall support
Signed-off-by: Sven Schnelle <svens@stackframe.org> Signed-off-by: Helge Deller <deller@gmx.de>
Diffstat (limited to 'arch/parisc')
-rw-r--r--arch/parisc/Kconfig13
-rw-r--r--arch/parisc/include/asm/fixmap.h1
-rw-r--r--arch/parisc/include/asm/kexec.h37
-rw-r--r--arch/parisc/kernel/Makefile1
-rw-r--r--arch/parisc/kernel/kexec.c105
-rw-r--r--arch/parisc/kernel/relocate_kernel.S149
6 files changed, 306 insertions, 0 deletions
diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig
index ee59171edffe..548c767f4358 100644
--- a/arch/parisc/Kconfig
+++ b/arch/parisc/Kconfig
@@ -346,6 +346,19 @@ config NR_CPUS
346 depends on SMP 346 depends on SMP
347 default "4" 347 default "4"
348 348
349config KEXEC
350 bool "Kexec system call"
351 select KEXEC_CORE
352 help
353 kexec is a system call that implements the ability to shutdown your
354 current kernel, and to start another kernel. It is like a reboot
355 but it is independent of the system firmware. And like a reboot
356 you can start any kernel with it, not just Linux.
357
358 It is an ongoing process to be certain the hardware in a machine
359 shutdown, so do not be surprised if this code does not
360 initially work for you.
361
349endmenu 362endmenu
350 363
351 364
diff --git a/arch/parisc/include/asm/fixmap.h b/arch/parisc/include/asm/fixmap.h
index 288da73d4cc0..e480b2c05407 100644
--- a/arch/parisc/include/asm/fixmap.h
+++ b/arch/parisc/include/asm/fixmap.h
@@ -30,6 +30,7 @@
30enum fixed_addresses { 30enum fixed_addresses {
31 /* Support writing RO kernel text via kprobes, jump labels, etc. */ 31 /* Support writing RO kernel text via kprobes, jump labels, etc. */
32 FIX_TEXT_POKE0, 32 FIX_TEXT_POKE0,
33 FIX_TEXT_KEXEC,
33 FIX_BITMAP_COUNT 34 FIX_BITMAP_COUNT
34}; 35};
35 36
diff --git a/arch/parisc/include/asm/kexec.h b/arch/parisc/include/asm/kexec.h
new file mode 100644
index 000000000000..a99ea747d7ed
--- /dev/null
+++ b/arch/parisc/include/asm/kexec.h
@@ -0,0 +1,37 @@
1/* SPDX-License-Identifier: GPL-2.0 */
2#ifndef _ASM_PARISC_KEXEC_H
3#define _ASM_PARISC_KEXEC_H
4
5#ifdef CONFIG_KEXEC
6
7/* Maximum physical address we can use pages from */
8#define KEXEC_SOURCE_MEMORY_LIMIT (-1UL)
9/* Maximum address we can reach in physical address mode */
10#define KEXEC_DESTINATION_MEMORY_LIMIT (-1UL)
11/* Maximum address we can use for the control code buffer */
12#define KEXEC_CONTROL_MEMORY_LIMIT (-1UL)
13
14#define KEXEC_CONTROL_PAGE_SIZE 4096
15
16#define KEXEC_ARCH KEXEC_ARCH_PARISC
17#define ARCH_HAS_KIMAGE_ARCH
18
19#ifndef __ASSEMBLY__
20
21struct kimage_arch {
22 unsigned long initrd_start;
23 unsigned long initrd_end;
24 unsigned long cmdline;
25};
26
27static inline void crash_setup_regs(struct pt_regs *newregs,
28 struct pt_regs *oldregs)
29{
30 /* Dummy implementation for now */
31}
32
33#endif /* __ASSEMBLY__ */
34
35#endif /* CONFIG_KEXEC */
36
37#endif /* _ASM_PARISC_KEXEC_H */
diff --git a/arch/parisc/kernel/Makefile b/arch/parisc/kernel/Makefile
index c232266b517c..487cf88866a8 100644
--- a/arch/parisc/kernel/Makefile
+++ b/arch/parisc/kernel/Makefile
@@ -37,3 +37,4 @@ obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
37obj-$(CONFIG_JUMP_LABEL) += jump_label.o 37obj-$(CONFIG_JUMP_LABEL) += jump_label.o
38obj-$(CONFIG_KGDB) += kgdb.o 38obj-$(CONFIG_KGDB) += kgdb.o
39obj-$(CONFIG_KPROBES) += kprobes.o 39obj-$(CONFIG_KPROBES) += kprobes.o
40obj-$(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
index 000000000000..a92d265a2261
--- /dev/null
+++ b/arch/parisc/kernel/kexec.c
@@ -0,0 +1,105 @@
1// SPDX-License-Identifier: GPL-2.0
2
3#include <linux/kernel.h>
4#include <linux/console.h>
5#include <linux/kexec.h>
6#include <linux/delay.h>
7#include <asm/cacheflush.h>
8#include <asm/sections.h>
9
10extern void relocate_new_kernel(unsigned long head,
11 unsigned long start,
12 unsigned long phys);
13
14extern const unsigned int relocate_new_kernel_size;
15extern unsigned int kexec_initrd_start_offset;
16extern unsigned int kexec_initrd_end_offset;
17extern unsigned int kexec_cmdline_offset;
18extern unsigned int kexec_free_mem_offset;
19
20static void kexec_show_segment_info(const struct kimage *kimage,
21 unsigned long n)
22{
23 pr_debug(" segment[%lu]: %016lx - %016lx, 0x%lx bytes, %lu pages\n",
24 n,
25 kimage->segment[n].mem,
26 kimage->segment[n].mem + kimage->segment[n].memsz,
27 (unsigned long)kimage->segment[n].memsz,
28 (unsigned long)kimage->segment[n].memsz / PAGE_SIZE);
29}
30
31static void kexec_image_info(const struct kimage *kimage)
32{
33 unsigned long i;
34
35 pr_debug("kexec kimage info:\n");
36 pr_debug(" type: %d\n", kimage->type);
37 pr_debug(" start: %lx\n", kimage->start);
38 pr_debug(" head: %lx\n", kimage->head);
39 pr_debug(" nr_segments: %lu\n", kimage->nr_segments);
40
41 for (i = 0; i < kimage->nr_segments; i++)
42 kexec_show_segment_info(kimage, i);
43}
44
45void machine_kexec_cleanup(struct kimage *kimage)
46{
47}
48
49void machine_crash_shutdown(struct pt_regs *regs)
50{
51}
52
53void machine_shutdown(void)
54{
55 smp_send_stop();
56 while (num_online_cpus() > 1) {
57 cpu_relax();
58 mdelay(1);
59 }
60}
61
62void machine_kexec(struct kimage *image)
63{
64#ifdef CONFIG_64BIT
65 Elf64_Fdesc desc;
66#endif
67 void (*reloc)(unsigned long head,
68 unsigned long start,
69 unsigned long phys);
70
71 unsigned long phys = page_to_phys(image->control_code_page);
72 void *virt = (void *)__fix_to_virt(FIX_TEXT_KEXEC);
73 struct kimage_arch *arch = &image->arch;
74
75 set_fixmap(FIX_TEXT_KEXEC, phys);
76
77 flush_cache_all();
78
79#ifdef CONFIG_64BIT
80 reloc = (void *)&desc;
81 desc.addr = (long long)virt;
82#else
83 reloc = (void *)virt;
84#endif
85
86 memcpy(virt, dereference_function_descriptor(relocate_new_kernel),
87 relocate_new_kernel_size);
88
89 *(unsigned long *)(virt + kexec_cmdline_offset) = arch->cmdline;
90 *(unsigned long *)(virt + kexec_initrd_start_offset) = arch->initrd_start;
91 *(unsigned long *)(virt + kexec_initrd_end_offset) = arch->initrd_end;
92 *(unsigned long *)(virt + kexec_free_mem_offset) = PAGE0->mem_free;
93
94 flush_cache_all();
95 flush_tlb_all();
96 local_irq_disable();
97
98 reloc(image->head & PAGE_MASK, image->start, phys);
99}
100
101int machine_kexec_prepare(struct kimage *image)
102{
103 kexec_image_info(image);
104 return 0;
105}
diff --git a/arch/parisc/kernel/relocate_kernel.S b/arch/parisc/kernel/relocate_kernel.S
new file mode 100644
index 000000000000..2561e52b8d9b
--- /dev/null
+++ b/arch/parisc/kernel/relocate_kernel.S
@@ -0,0 +1,149 @@
1/* SPDX-License-Identifier: GPL-2.0 */
2#include <linux/linkage.h>
3#include <linux/kexec.h>
4
5#include <asm/assembly.h>
6#include <asm/asm-offsets.h>
7#include <asm/page.h>
8#include <asm/setup.h>
9#include <asm/psw.h>
10
11.level PA_ASM_LEVEL
12
13.macro kexec_param name
14.align 8
15ENTRY(kexec\()_\name)
16#ifdef CONFIG_64BIT
17 .dword 0
18#else
19 .word 0
20#endif
21
22ENTRY(kexec\()_\name\()_offset)
23 .word kexec\()_\name - relocate_new_kernel
24.endm
25
26.text
27
28/* args:
29 * r26 - kimage->head
30 * r25 - start address of kernel
31 * r24 - physical address of relocate code
32 */
33
34ENTRY_CFI(relocate_new_kernel)
350: copy %arg1, %rp
36 /* disable I and Q bit, so we are allowed to execute RFI */
37 rsm PSW_SM_I, %r0
38 nop
39 nop
40 nop
41 nop
42 nop
43 nop
44 nop
45
46 rsm PSW_SM_Q, %r0
47 nop
48 nop
49 nop
50 nop
51 nop
52 nop
53 nop
54
55 /*
56 * After return-from-interrupt, we want to run without Code/Data
57 * translation enabled just like on a normal boot.
58 */
59
60 /* calculate new physical execution address */
61 ldo 1f-0b(%arg2), %r1
62 mtctl %r0, %cr17 /* IIASQ */
63 mtctl %r0, %cr17 /* IIASQ */
64 mtctl %r1, %cr18 /* IIAOQ */
65 ldo 4(%r1),%r1
66 mtctl %r1, %cr18 /* IIAOQ */
67#ifdef CONFIG_64BIT
68 depdi,z 1, PSW_W_BIT, 1, %r1
69 mtctl %r1, %cr22 /* IPSW */
70#else
71 mtctl %r0, %cr22 /* IPSW */
72#endif
73 /* lets go... */
74 rfi
751: nop
76 nop
77
78.Lloop:
79 LDREG,ma REG_SZ(%arg0), %r3
80 /* If crash kernel, no copy needed */
81 cmpib,COND(=),n 0,%r3,boot
82
83 bb,<,n %r3, 31 - IND_DONE_BIT, boot
84 bb,>=,n %r3, 31 - IND_INDIRECTION_BIT, .Lnotind
85 /* indirection, load and restart */
86 movb %r3, %arg0, .Lloop
87 depi 0, 31, PAGE_SHIFT, %arg0
88
89.Lnotind:
90 bb,>=,n %r3, 31 - IND_DESTINATION_BIT, .Lnotdest
91 b .Lloop
92 copy %r3, %r20
93
94.Lnotdest:
95 bb,>= %r3, 31 - IND_SOURCE_BIT, .Lloop
96 depi 0, 31, PAGE_SHIFT, %r3
97 copy %r3, %r21
98
99 /* copy page */
100 copy %r0, %r18
101 zdepi 1, 31 - PAGE_SHIFT, 1, %r18
102 add %r20, %r18, %r17
103
104 depi 0, 31, PAGE_SHIFT, %r20
105.Lcopy:
106 copy %r20, %r12
107 LDREG,ma REG_SZ(%r21), %r8
108 LDREG,ma REG_SZ(%r21), %r9
109 LDREG,ma REG_SZ(%r21), %r10
110 LDREG,ma REG_SZ(%r21), %r11
111 STREG,ma %r8, REG_SZ(%r20)
112 STREG,ma %r9, REG_SZ(%r20)
113 STREG,ma %r10, REG_SZ(%r20)
114 STREG,ma %r11, REG_SZ(%r20)
115
116#ifndef CONFIG_64BIT
117 LDREG,ma REG_SZ(%r21), %r8
118 LDREG,ma REG_SZ(%r21), %r9
119 LDREG,ma REG_SZ(%r21), %r10
120 LDREG,ma REG_SZ(%r21), %r11
121 STREG,ma %r8, REG_SZ(%r20)
122 STREG,ma %r9, REG_SZ(%r20)
123 STREG,ma %r10, REG_SZ(%r20)
124 STREG,ma %r11, REG_SZ(%r20)
125#endif
126
127 fdc %r0(%r12)
128 cmpb,COND(<<) %r20,%r17,.Lcopy
129 fic (%sr4, %r12)
130 b,n .Lloop
131
132boot:
133 mtctl %r0, %cr15
134
135 LDREG kexec_free_mem-0b(%arg2), %arg0
136 LDREG kexec_cmdline-0b(%arg2), %arg1
137 LDREG kexec_initrd_end-0b(%arg2), %arg3
138 LDREG kexec_initrd_start-0b(%arg2), %arg2
139 bv,n %r0(%rp)
140
141ENDPROC_CFI(relocate_new_kernel);
142
143ENTRY(relocate_new_kernel_size)
144 .word relocate_new_kernel_size - relocate_new_kernel
145
146kexec_param cmdline
147kexec_param initrd_start
148kexec_param initrd_end
149kexec_param free_mem