[PATCH 1/3] LoongArch: Add kexec support

Youling Tang tangyouling at loongson.cn
Sun Sep 4 18:01:06 PDT 2022



On 08/29/2022 12:37 PM, Youling Tang wrote:
> Add three new files, kexec.h, machine_kexec.c and relocate_kernel.S to the
> LoongArch architecture that add support for the kexec re-boot mechanis
> (CONFIG_KEXEC) on LoongArch platforms.
>
> Supports loading vmlinux (vmlinux.elf) in ELF format and vmlinux.efi in
> PE format.
>
> I tested this on  LoongArch 3A5000 machine and works as expected,
>
>  $ sudo kexec -l /boot/vmlinux.efi --reuse-cmdline
>  $ sudo kexec -e
>
> Signed-off-by: Youling Tang <tangyouling at loongson.cn>
> ---
>  arch/loongarch/Kconfig                  |  11 ++
>  arch/loongarch/include/asm/kexec.h      |  58 ++++++++
>  arch/loongarch/kernel/Makefile          |   2 +
>  arch/loongarch/kernel/head.S            |   7 +-
>  arch/loongarch/kernel/machine_kexec.c   | 178 ++++++++++++++++++++++++
>  arch/loongarch/kernel/relocate_kernel.S | 125 +++++++++++++++++
>  6 files changed, 380 insertions(+), 1 deletion(-)
>  create mode 100644 arch/loongarch/include/asm/kexec.h
>  create mode 100644 arch/loongarch/kernel/machine_kexec.c
>  create mode 100644 arch/loongarch/kernel/relocate_kernel.S
>
> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
> index 45364cffc793..903c82fa958d 100644
> --- a/arch/loongarch/Kconfig
> +++ b/arch/loongarch/Kconfig
> @@ -409,6 +409,17 @@ config FORCE_MAX_ZONEORDER
>  	  The page size is not necessarily 4KB.  Keep this in mind
>  	  when choosing a value for this option.
>
> +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.
> +
> +	  The name comes from the similarity to the exec system call.
> +
>  config SECCOMP
>  	bool "Enable seccomp to safely compute untrusted bytecode"
>  	depends on PROC_FS
> diff --git a/arch/loongarch/include/asm/kexec.h b/arch/loongarch/include/asm/kexec.h
> new file mode 100644
> index 000000000000..5c9e7b5eccb8
> --- /dev/null
> +++ b/arch/loongarch/include/asm/kexec.h
> @@ -0,0 +1,58 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * kexec.h for kexec
> + *
> + * Copyright (C) 2022 Loongson Technology Corporation Limited
> + */
> +
> +#ifndef _ASM_KEXEC_H
> +#define _ASM_KEXEC_H
> +
> +#include <asm/stacktrace.h>
> +#include <asm/page.h>
> +
> +/* 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)
> +
> +/* Reserve a page for the control code buffer */
> +#define KEXEC_CONTROL_PAGE_SIZE PAGE_SIZE
> +
> +/* The native architecture */
> +#define KEXEC_ARCH KEXEC_ARCH_LOONGARCH
> +
> +static inline void crash_setup_regs(struct pt_regs *newregs,
> +				    struct pt_regs *oldregs)
> +{
> +	if (oldregs)
> +		memcpy(newregs, oldregs, sizeof(*newregs));
> +	else
> +		prepare_frametrace(newregs);
> +}
> +
> +#define ARCH_HAS_KIMAGE_ARCH
> +
> +struct kimage_arch {
> +	unsigned long boot_flag;
> +	unsigned long fdt_addr;
> +};
> +
> +typedef void (*do_kexec_t)(unsigned long boot_flag,
> +			   unsigned long fdt_addr,
> +			   unsigned long first_ind_entry,
> +			   unsigned long jump_addr);
> +
> +struct kimage;
> +extern const unsigned char relocate_new_kernel[];
> +extern const size_t relocate_new_kernel_size;
> +
> +#ifdef CONFIG_SMP
> +extern atomic_t kexec_ready_to_reboot;
> +extern const unsigned char kexec_smp_wait[];
> +extern void kexec_reboot(void);
> +#endif
> +
> +#endif /* !_ASM_KEXEC_H */
> diff --git a/arch/loongarch/kernel/Makefile b/arch/loongarch/kernel/Makefile
> index a213e994db68..20b64ac3f128 100644
> --- a/arch/loongarch/kernel/Makefile
> +++ b/arch/loongarch/kernel/Makefile
> @@ -17,6 +17,8 @@ obj-$(CONFIG_CPU_HAS_FPU)	+= fpu.o
>  obj-$(CONFIG_MODULES)		+= module.o module-sections.o
>  obj-$(CONFIG_STACKTRACE)	+= stacktrace.o
>
> +obj-$(CONFIG_KEXEC)             += machine_kexec.o relocate_kernel.o
> +
>  obj-$(CONFIG_PROC_FS)		+= proc.o
>
>  obj-$(CONFIG_SMP)		+= smp.o
> diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S
> index 01bac62a6442..22bdf4928325 100644
> --- a/arch/loongarch/kernel/head.S
> +++ b/arch/loongarch/kernel/head.S
> @@ -20,7 +20,12 @@
>
>  _head:
>  	.word	MZ_MAGIC		/* "MZ", MS-DOS header */
> -	.org	0x3c			/* 0x04 ~ 0x3b reserved */
> +	.org	0x8
> +	.quad	0			/* Image load offset from start of RAM */
> +	.dword	_end - _text		/* Effective size of kernel image */
> +	.quad	0
> +	.dword	kernel_entry		/* Kernel entry point */
> +	.org	0x3c			/* 0x28 ~ 0x3b reserved */
>  	.long	pe_header - _head	/* Offset to the PE header */
>
>  pe_header:
> diff --git a/arch/loongarch/kernel/machine_kexec.c b/arch/loongarch/kernel/machine_kexec.c
> new file mode 100644
> index 000000000000..4ffcd4cd9c8c
> --- /dev/null
> +++ b/arch/loongarch/kernel/machine_kexec.c
> @@ -0,0 +1,178 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * machine_kexec.c for kexec
> + *
> + * Copyright (C) 2022 Loongson Technology Corporation Limited
> + */
> +#include <linux/compiler.h>
> +#include <linux/cpu.h>
> +#include <linux/kexec.h>
> +#include <linux/mm.h>
> +#include <linux/delay.h>
> +#include <linux/libfdt.h>
> +#include <linux/of_fdt.h>
> +
> +#include <asm/bootinfo.h>
> +#include <asm/cacheflush.h>
> +#include <asm/page.h>
> +
> +/* 0x100000 ~ 0x200000 is safe */
> +#define KEXEC_CTRL_CODE	TO_CACHE(0x100000UL)
> +#define KEXEC_BLOB_ADDR	TO_CACHE(0x108000UL)
> +
> +static unsigned long reboot_code_buffer;
> +#ifdef CONFIG_SMP
> +void (*relocated_kexec_smp_wait)(void *);
> +atomic_t kexec_ready_to_reboot = ATOMIC_INIT(0);
> +#endif
> +
> +static unsigned long jump_addr;
> +static unsigned long first_ind_entry;
> +static unsigned long boot_flag;
> +static unsigned long fdt_addr;
> +
> +static void kexec_image_info(const struct kimage *kimage)
> +{
> +	unsigned long i;
> +
> +	pr_debug("kexec kimage info:\n");
> +	pr_debug("\ttype:        %d\n", kimage->type);
> +	pr_debug("\tstart:       %lx\n", kimage->start);
> +	pr_debug("\thead:        %lx\n", kimage->head);
> +	pr_debug("\tnr_segments: %lu\n", kimage->nr_segments);
> +
> +	for (i = 0; i < kimage->nr_segments; i++) {
> +		pr_debug("\t    segment[%lu]: %016lx - %016lx", i,
> +			kimage->segment[i].mem,
> +			kimage->segment[i].mem + kimage->segment[i].memsz);
> +		pr_debug("\t\t0x%lx bytes, %lu pages\n",
> +			(unsigned long)kimage->segment[i].memsz,
> +			(unsigned long)kimage->segment[i].memsz /  PAGE_SIZE);
> +	}
> +}
> +
> +int machine_kexec_prepare(struct kimage *kimage)
> +{
> +	int i;
> +	void *dtb = (void *)KEXEC_BLOB_ADDR;
> +
> +	kexec_image_info(kimage);
> +
> +	/* Find the Flattened Device Tree */
> +	for (i = 0; i < kimage->nr_segments; i++) {
> +		if (!fdt_check_header(kimage->segment[i].buf)) {
> +			memcpy(dtb, kimage->segment[i].buf, SZ_64K);
> +			kimage->arch.boot_flag = fw_arg0;
> +			kimage->arch.fdt_addr = (unsigned long) dtb;
> +			break;
> +		}
> +		continue;
> +	}
> +
> +	/* kexec need a safe page to save reboot_code_buffer */
> +	kimage->control_code_page = virt_to_page((void *)KEXEC_CTRL_CODE);
> +
> +	reboot_code_buffer =
> +	  (unsigned long)page_address(kimage->control_code_page);
> +	memcpy((void *)reboot_code_buffer, relocate_new_kernel,
> +	       relocate_new_kernel_size);
> +
> +	/* All secondary cpus now may jump to kexec_smp_wait cycle */
> +	relocated_kexec_smp_wait = reboot_code_buffer +
> +		(void *)(kexec_smp_wait - relocate_new_kernel);
> +
> +	return 0;
> +}
> +
> +void machine_kexec_cleanup(struct kimage *kimage)
> +{
> +}
> +
> +#ifdef CONFIG_SMP
> +void kexec_reboot(void)
> +{
> +	do_kexec_t do_kexec = NULL;
> +
> +	/* All secondary cpus go to kexec_smp_wait */
> +	if (smp_processor_id() > 0) {
> +		relocated_kexec_smp_wait(NULL);
> +		unreachable();
> +	}
> +
> +	do_kexec = (void *)reboot_code_buffer;
> +	do_kexec(boot_flag, fdt_addr, first_ind_entry, jump_addr);
> +
> +	unreachable();
> +}

self-check,
kexec_reboot() is in! SMP needs to be used, modified as follows:

void kexec_reboot(void)
{
         do_kexec_t do_kexec = NULL;

#ifdef CONFIG_SMP
         /* All secondary cpus go to kexec_smp_wait */
         if (smp_processor_id() > 0) {
                 relocated_kexec_smp_wait(NULL);
                 unreachable();
         }
#endif

         do_kexec = (void *)reboot_code_buffer;
         do_kexec(boot_flag, fdt_addr, first_ind_entry, jump_addr);

         unreachable();
}

Youling




More information about the kexec mailing list