lkml.org 
[lkml]   [2022]   [Aug]   [29]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 1/3] LoongArch: Add kexec support
    Date
    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@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();
    +}
    +
    +static void kexec_shutdown_secondary(void *)
    +{
    + local_irq_disable();
    + while (!atomic_read(&kexec_ready_to_reboot))
    + cpu_relax();
    +
    + kexec_reboot();
    +}
    +
    +void machine_crash_shutdown(struct pt_regs *regs)
    +{
    +}
    +#endif
    +
    +void machine_shutdown(void)
    +{
    + smp_call_function(kexec_shutdown_secondary, NULL, 0);
    +}
    +
    +void machine_kexec(struct kimage *image)
    +{
    + unsigned long entry;
    + unsigned long *ptr;
    + struct kimage_arch *internal = &image->arch;
    +
    + boot_flag = internal->boot_flag;
    + fdt_addr = internal->fdt_addr;
    +
    + jump_addr = (unsigned long)phys_to_virt(image->start);
    +
    + first_ind_entry = (unsigned long)phys_to_virt(image->head & PAGE_MASK);
    +
    + /*
    + * The generic kexec code builds a page list with physical
    + * addresses. they are directly accessible through XKPRANGE
    + * hence the phys_to_virt() call.
    + */
    + for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE);
    + ptr = (entry & IND_INDIRECTION) ?
    + phys_to_virt(entry & PAGE_MASK) : ptr + 1) {
    + if (*ptr & IND_SOURCE || *ptr & IND_INDIRECTION ||
    + *ptr & IND_DESTINATION)
    + *ptr = (unsigned long) phys_to_virt(*ptr);
    + }
    +
    + /* Mark offline before disabling local irq. */
    + set_cpu_online(smp_processor_id(), false);
    +
    + /* we do not want to be bothered. */
    + local_irq_disable();
    +
    + pr_notice("Will call new kernel at %lx\n", jump_addr);
    + pr_notice("FDT image at %lx\n", fdt_addr);
    + pr_notice("Bye ...\n");
    +
    + /* Make reboot code buffer available to the boot CPU. */
    + flush_cache_all();
    +
    + atomic_set(&kexec_ready_to_reboot, 1);
    +
    + /*
    + * We know we were online, and there will be no incoming IPIs at
    + * this point.
    + */
    + set_cpu_online(smp_processor_id(), true);
    +
    + /* Ensure remote CPUs observe that we're online before rebooting. */
    + smp_mb__after_atomic();
    +
    + kexec_reboot();
    +}
    diff --git a/arch/loongarch/kernel/relocate_kernel.S b/arch/loongarch/kernel/relocate_kernel.S
    new file mode 100644
    index 000000000000..d1f242f74ea8
    --- /dev/null
    +++ b/arch/loongarch/kernel/relocate_kernel.S
    @@ -0,0 +1,125 @@
    +/* SPDX-License-Identifier: GPL-2.0 */
    +/*
    + * relocate_kernel.S for kexec
    + *
    + * Copyright (C) 2022 Loongson Technology Corporation Limited
    + */
    +
    +#include <linux/kexec.h>
    +
    +#include <asm/asm.h>
    +#include <asm/asmmacro.h>
    +#include <asm/regdef.h>
    +#include <asm/loongarch.h>
    +#include <asm/stackframe.h>
    +#include <asm/addrspace.h>
    +
    +#define IPI_REG_BASE 0x1fe01000
    +
    +SYM_CODE_START(relocate_new_kernel)
    + /*
    + * s0: Boot flag passed to the new kernel
    + * s1: Virt address of the FDT image
    + * s2: Pointer to the current entry
    + * s3: Virt address to jump to after relocation
    + */
    + move s0, a0
    + move s1, a1
    + move s2, a2
    + move s3, a3
    +
    +process_entry:
    + PTR_L s4, s2, 0
    + PTR_ADDI s2, s2, SZREG
    +
    + /* destination page */
    + andi s5, s4, IND_DESTINATION
    + beqz s5, 1f
    + li.w t0, ~0x1
    + and s6, s4, t0 /* store destination addr in s6 */
    + b process_entry
    +
    +1:
    + /* indirection page, update s2 */
    + andi s5, s4, IND_INDIRECTION
    + beqz s5, 1f
    + li.w t0, ~0x2
    + and s2, s4, t0
    + b process_entry
    +
    +1:
    + /* done page */
    + andi s5, s4, IND_DONE
    + beqz s5, 1f
    + b done
    +1:
    + /* source page */
    + andi s5, s4, IND_SOURCE
    + beqz s5, process_entry
    + li.w t0, ~0x8
    + and s4, s4, t0
    + li.w s8, (1 << _PAGE_SHIFT) / SZREG
    +
    +copy_word:
    + /* copy page word by word */
    + REG_L s7, s4, 0
    + REG_S s7, s6, 0
    + PTR_ADDI s6, s6, SZREG
    + PTR_ADDI s4, s4, SZREG
    + LONG_ADDI s8, s8, -1
    + beqz s8, process_entry
    + b copy_word
    + b process_entry
    +
    +done:
    + dbar 0
    +
    + move a0, s0
    + move a1, s1
    + /* jump to the new kernel */
    + jr s3
    +SYM_CODE_END(relocate_new_kernel)
    +
    +#ifdef CONFIG_SMP
    +/*
    + * Other CPUs should wait until code is relocated and
    + * then start at entry (?) point.
    + */
    +SYM_CODE_START(kexec_smp_wait)
    + li.d t0, IPI_REG_BASE
    + li.d t1, UNCACHE_BASE
    + or t0, t0, t1
    +
    + /*
    + * s1:initfn
    + * t0:base t1:cpuid t2:node t3:core t4:count
    + */
    + csrrd t1, LOONGARCH_CSR_CPUID
    + andi t1, t1, CSR_CPUID_COREID
    + andi t3, t1, 0x3
    + slli.w t3, t3, 8 /* get core id */
    + or t0, t0, t3
    + andi t2, t1, 0x3c
    + slli.d t2, t2, 42 /* get node id */
    + or t0, t0, t2
    +
    +1: li.w t4, 0x100 /* wait for init loop */
    +2: addi.w t4, t4, -1 /* limit mailbox access */
    + bnez t4, 2b
    + ld.w s1, t0, 0x20 /* check PC as an indicator */
    + beqz s1, 1b
    + ld.d s1, t0, 0x20 /* get PC via mailbox */
    + ld.d sp, t0, 0x28 /* get SP via mailbox */
    + ld.d tp, t0, 0x30 /* get TP via mailbox */
    +
    + li.d t0, CACHE_BASE
    + or s1, s1, t0
    + jr s1 /* jump to initial PC */
    +SYM_CODE_END(kexec_smp_wait)
    +#endif
    +
    +relocate_new_kernel_end:
    +
    +SYM_DATA_START(relocate_new_kernel_size)
    + PTR relocate_new_kernel_end - relocate_new_kernel
    +SYM_DATA_END(relocate_new_kernel_size)
    --
    2.36.0
    \
     
     \ /
      Last update: 2022-08-29 06:38    [W:4.194 / U:0.012 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site