lkml.org 
[lkml]   [2016]   [Aug]   [12]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v2 2/6] powerpc: kexec_file: Add buffer hand-over support for the next kernel
    Date
    The buffer hand-over mechanism allows the currently running kernel to pass
    data to kernel that will be kexec'd via a kexec segment. The second kernel
    can check whether the previous kernel sent data and retrieve it.

    This is the architecture-specific part.

    Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
    ---
    arch/powerpc/include/asm/kexec.h | 12 +++-
    arch/powerpc/kernel/kexec_elf_64.c | 2 +-
    arch/powerpc/kernel/machine_kexec_64.c | 114 +++++++++++++++++++++++++++++++--
    3 files changed, 120 insertions(+), 8 deletions(-)

    diff --git a/arch/powerpc/include/asm/kexec.h b/arch/powerpc/include/asm/kexec.h
    index 31bc64e07c8f..b20738df26f8 100644
    --- a/arch/powerpc/include/asm/kexec.h
    +++ b/arch/powerpc/include/asm/kexec.h
    @@ -92,12 +92,20 @@ static inline bool kdump_in_progress(void)
    }

    #ifdef CONFIG_KEXEC_FILE
    +#define ARCH_HAS_KIMAGE_ARCH
    +
    +struct kimage_arch {
    + phys_addr_t handover_buffer_addr;
    + unsigned long handover_buffer_size;
    +};
    +
    int setup_purgatory(struct kimage *image, const void *slave_code,
    const void *fdt, unsigned long kernel_load_addr,
    unsigned long fdt_load_addr, unsigned long stack_top,
    int debug);
    -int setup_new_fdt(void *fdt, unsigned long initrd_load_addr,
    - unsigned long initrd_len, const char *cmdline);
    +int setup_new_fdt(const struct kimage *image, void *fdt,
    + unsigned long initrd_load_addr, unsigned long initrd_len,
    + const char *cmdline);
    bool find_debug_console(const void *fdt, int chosen_node);
    int merge_partial_dtb(void *to, const void *from);
    #endif /* CONFIG_KEXEC_FILE */
    diff --git a/arch/powerpc/kernel/kexec_elf_64.c b/arch/powerpc/kernel/kexec_elf_64.c
    index 1b902ad66e2a..22afc7b5ee73 100644
    --- a/arch/powerpc/kernel/kexec_elf_64.c
    +++ b/arch/powerpc/kernel/kexec_elf_64.c
    @@ -219,7 +219,7 @@ void *elf64_load(struct kimage *image, char *kernel_buf,
    }
    }

    - ret = setup_new_fdt(fdt, initrd_load_addr, initrd_len, cmdline);
    + ret = setup_new_fdt(image, fdt, initrd_load_addr, initrd_len, cmdline);
    if (ret)
    goto out;

    diff --git a/arch/powerpc/kernel/machine_kexec_64.c b/arch/powerpc/kernel/machine_kexec_64.c
    index a484a6346146..190c652e49b7 100644
    --- a/arch/powerpc/kernel/machine_kexec_64.c
    +++ b/arch/powerpc/kernel/machine_kexec_64.c
    @@ -490,6 +490,60 @@ int arch_kimage_file_post_load_cleanup(struct kimage *image)
    return image->fops->cleanup(image->image_loader_data);
    }

    +bool kexec_can_hand_over_buffer(void)
    +{
    + return true;
    +}
    +
    +int arch_kexec_add_handover_buffer(struct kimage *image,
    + unsigned long load_addr, unsigned long size)
    +{
    + image->arch.handover_buffer_addr = load_addr;
    + image->arch.handover_buffer_size = size;
    +
    + return 0;
    +}
    +
    +int kexec_get_handover_buffer(void **addr, unsigned long *size)
    +{
    + int ret;
    + u64 start_addr, end_addr;
    +
    + ret = of_property_read_u64(of_chosen,
    + "linux,kexec-handover-buffer-start",
    + &start_addr);
    + if (ret == -EINVAL)
    + return -ENOENT;
    + else if (ret)
    + return -EINVAL;
    +
    + ret = of_property_read_u64(of_chosen, "linux,kexec-handover-buffer-end",
    + &end_addr);
    + if (ret == -EINVAL)
    + return -ENOENT;
    + else if (ret)
    + return -EINVAL;
    +
    + *addr = __va(start_addr);
    + /* -end is the first address after the buffer. */
    + *size = end_addr - start_addr;
    +
    + return 0;
    +}
    +
    +int kexec_free_handover_buffer(void)
    +{
    + int ret;
    + void *addr;
    + unsigned long size;
    +
    + ret = kexec_get_handover_buffer(&addr, &size);
    + if (ret)
    + return ret;
    +
    + return memblock_free((phys_addr_t) addr, size);
    +}
    +
    /**
    * arch_kexec_walk_mem() - call func(data) for each unreserved memory block
    * @kbuf: Context info for the search. Also passed to @func.
    @@ -687,9 +741,52 @@ int setup_purgatory(struct kimage *image, const void *slave_code,
    return 0;
    }

    -/*
    - * setup_new_fdt() - modify /chosen and memory reservation for the next kernel
    - * @fdt:
    +/**
    + * setup_handover_buffer() - add properties and reservation for the handover buffer
    + * @image: kexec image being loaded.
    + * @fdt: Flattened device tree for the next kernel.
    + * @chosen_node: Offset to the chosen node.
    + *
    + * Return: 0 on success, negative errno on error.
    + */
    +static int setup_handover_buffer(const struct kimage *image, void *fdt,
    + int chosen_node)
    +{
    + int ret;
    +
    + if (image->arch.handover_buffer_addr == 0)
    + return 0;
    +
    + ret = fdt_setprop_u64(fdt, chosen_node,
    + "linux,kexec-handover-buffer-start",
    + image->arch.handover_buffer_addr);
    + if (ret < 0)
    + return -EINVAL;
    +
    + /* -end is the first address after the buffer. */
    + ret = fdt_setprop_u64(fdt, chosen_node,
    + "linux,kexec-handover-buffer-end",
    + image->arch.handover_buffer_addr +
    + image->arch.handover_buffer_size);
    + if (ret < 0)
    + return -EINVAL;
    +
    + ret = fdt_add_mem_rsv(fdt, image->arch.handover_buffer_addr,
    + image->arch.handover_buffer_size);
    + if (ret)
    + return -EINVAL;
    +
    + pr_debug("kexec handover buffer at 0x%llx, size = 0x%lx\n",
    + image->arch.handover_buffer_addr,
    + image->arch.handover_buffer_size);
    +
    + return 0;
    +}
    +
    +/**
    + * setup_new_fdt() - modify /chosen and memory reservations for the next kernel
    + * @image: kexec image being loaded.
    + * @fdt: Flattened device tree for the next kernel.
    * @initrd_load_addr: Address where the next initrd will be loaded.
    * @initrd_len: Size of the next initrd, or 0 if there will be none.
    * @cmdline: Command line for the next kernel, or NULL if there will
    @@ -697,8 +794,9 @@ int setup_purgatory(struct kimage *image, const void *slave_code,
    *
    * Return: 0 on success, or negative errno on error.
    */
    -int setup_new_fdt(void *fdt, unsigned long initrd_load_addr,
    - unsigned long initrd_len, const char *cmdline)
    +int setup_new_fdt(const struct kimage *image, void *fdt,
    + unsigned long initrd_load_addr, unsigned long initrd_len,
    + const char *cmdline)
    {
    uint64_t oldfdt_addr;
    int i, ret, chosen_node;
    @@ -847,6 +945,12 @@ int setup_new_fdt(void *fdt, unsigned long initrd_load_addr,
    }
    }

    + ret = setup_handover_buffer(image, fdt, chosen_node);
    + if (ret) {
    + pr_err("Error setting up the new device tree.\n");
    + return ret;
    + }
    +
    ret = fdt_setprop(fdt, chosen_node, "linux,booted-from-kexec", NULL, 0);
    if (ret) {
    pr_err("Error setting up the new device tree.\n");
    --
    1.9.1
    \
     
     \ /
      Last update: 2016-09-17 09:56    [W:4.060 / U:0.016 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site