[PATCH 2/6] powerpc: kexec_file: Add buffer hand-over support for the next kernel

From: Thiago Jung Bauermann
Date: Mon Jun 20 2016 - 23:23:25 EST


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@xxxxxxxxxxxxxxxxxx>
---
arch/powerpc/include/asm/kexec.h | 9 +++++
arch/powerpc/kernel/kexec_elf_64.c | 44 +++++++++++++++++++++++
arch/powerpc/kernel/machine_kexec_64.c | 64 ++++++++++++++++++++++++++++++++++
3 files changed, 117 insertions(+)

diff --git a/arch/powerpc/include/asm/kexec.h b/arch/powerpc/include/asm/kexec.h
index a46f5f45570c..9b1ff59bc188 100644
--- a/arch/powerpc/include/asm/kexec.h
+++ b/arch/powerpc/include/asm/kexec.h
@@ -55,6 +55,15 @@ typedef void (*crash_shutdown_t)(void);

#ifdef CONFIG_KEXEC

+#ifdef CONFIG_KEXEC_FILE
+#define ARCH_HAS_KIMAGE_ARCH
+
+struct kimage_arch {
+ phys_addr_t handover_buffer_addr;
+ unsigned long handover_buffer_size;
+};
+#endif
+
/*
* This function is responsible for capturing register states if coming
* via panic or invoking dump using sysrq-trigger.
diff --git a/arch/powerpc/kernel/kexec_elf_64.c b/arch/powerpc/kernel/kexec_elf_64.c
index 4e71595300ed..5d2b7036fee7 100644
--- a/arch/powerpc/kernel/kexec_elf_64.c
+++ b/arch/powerpc/kernel/kexec_elf_64.c
@@ -96,6 +96,46 @@ static int elf64_probe(const char *buf, unsigned long len)
return elf_check_arch(&ehdr)? 0 : -ENOEXEC;
}

+static int setup_handover_buffer(struct kimage *image, void *fdt,
+ int chosen_node)
+{
+ int ret;
+
+ if (image->arch.handover_buffer_addr) {
+ ret = fdt_setprop_u64(fdt, chosen_node,
+ "linux,kexec-handover-buffer-start",
+ image->arch.handover_buffer_addr);
+ if (ret < 0) {
+ pr_err("Error setting up the new device tree.\n");
+ 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) {
+ pr_err("Error setting up the new device tree.\n");
+ return -EINVAL;
+ }
+
+ ret = fdt_add_mem_rsv(fdt, image->arch.handover_buffer_addr,
+ image->arch.handover_buffer_size);
+ if (ret) {
+ pr_err("Error reserving kexec handover buffer: %s\n",
+ fdt_strerror(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;
+}
+
static bool find_debug_console(void *fdt, int chosen_node)
{
int len;
@@ -494,6 +534,10 @@ void *elf64_load(struct kimage *image, char *kernel_buf,
}
}

+ ret = setup_handover_buffer(image, fdt, chosen_node);
+ if (ret)
+ goto out;
+
ret = fdt_setprop(fdt, chosen_node, "linux,booted-from-kexec", NULL, 0);
if (ret) {
pr_err("Error setting up the new device tree.\n");
diff --git a/arch/powerpc/kernel/machine_kexec_64.c b/arch/powerpc/kernel/machine_kexec_64.c
index 43e8185ab6f7..c582abf726f5 100644
--- a/arch/powerpc/kernel/machine_kexec_64.c
+++ b/arch/powerpc/kernel/machine_kexec_64.c
@@ -19,6 +19,7 @@
#include <linux/cpu.h>
#include <linux/hardirq.h>
#include <linux/memblock.h>
+#include <linux/libfdt.h>

#include <asm/page.h>
#include <asm/current.h>
@@ -481,6 +482,69 @@ 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 chosen_node;
+ int startsz, endsz;
+ const void *startp, *endp;
+ unsigned long start_addr, end_addr;
+
+ chosen_node = fdt_path_offset(initial_boot_params, "/chosen");
+ if (chosen_node < 0) {
+ pr_err("Malformed device tree: /chosen not found.\n");
+ return -EINVAL;
+ }
+
+ startp = of_get_flat_dt_prop(chosen_node,
+ "linux,kexec-handover-buffer-start",
+ &startsz);
+ endp = of_get_flat_dt_prop(chosen_node,
+ "linux,kexec-handover-buffer-end",
+ &endsz);
+ if (!startp || !endp) {
+ pr_debug("kexec handover buffer not found in the device tree.\n");
+ return -ENOENT;
+ }
+
+ start_addr = of_read_number(startp, startsz/4);
+ end_addr = of_read_number(endp, endsz/4);
+
+ *addr = __va(start_addr);
+ /* -end is the first address after the buffer. */
+ *size = end_addr - start_addr;
+
+ pr_debug("kexec handover buffer at %p, size = %lu\n", *addr, *size);
+
+ 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
* @image_type: kimage.type
--
1.9.1