parisc: add kexec syscall support
Signed-off-by: Sven Schnelle <svens@stackframe.org> Signed-off-by: Helge Deller <deller@gmx.de>
This commit is contained in:
parent
507efd63d9
commit
fc697dc0c2
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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 */
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue