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 <[email protected]>
---
 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


_______________________________________________
kexec mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/kexec

Reply via email to