Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fc697dc0 authored by Sven Schnelle's avatar Sven Schnelle Committed by Helge Deller
Browse files

parisc: add kexec syscall support



Signed-off-by: default avatarSven Schnelle <svens@stackframe.org>
Signed-off-by: default avatarHelge Deller <deller@gmx.de>
parent 507efd63
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -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


+1 −0
Original line number Diff line number Diff line
@@ -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
};

+37 −0
Original line number Diff line number Diff line
/* 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 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
// 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;
}
Loading