Skip to content

Commit

Permalink
parisc: add kexec syscall support
Browse files Browse the repository at this point in the history
Signed-off-by: Sven Schnelle <[email protected]>
Signed-off-by: Helge Deller <[email protected]>
  • Loading branch information
svenschnelle authored and hdeller committed Sep 8, 2019
1 parent 507efd6 commit fc697dc
Show file tree
Hide file tree
Showing 7 changed files with 307 additions and 0 deletions.
13 changes: 13 additions & 0 deletions arch/parisc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down
1 change: 1 addition & 0 deletions arch/parisc/include/asm/fixmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
};

Expand Down
37 changes: 37 additions & 0 deletions arch/parisc/include/asm/kexec.h
Original file line number Diff line number Diff line change
@@ -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 */
1 change: 1 addition & 0 deletions arch/parisc/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
105 changes: 105 additions & 0 deletions arch/parisc/kernel/kexec.c
Original file line number Diff line number Diff line change
@@ -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;
}
149 changes: 149 additions & 0 deletions arch/parisc/kernel/relocate_kernel.S
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions include/uapi/linux/kexec.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit fc697dc

Please sign in to comment.