Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1728563 > unrolled thread
| Started by | AKASHI Takahiro <takahiro.akashi@linaro.org> |
|---|---|
| First post | 2017-09-08 05:20 +0200 |
| Last post | 2017-09-08 05:20 +0200 |
| Articles | 6 — 1 participant |
Back to article view | Back to linux.kernel
[PATCH 0/9] kexec: add kexec_file_load() support AKASHI Takahiro <takahiro.akashi@linaro.org> - 2017-09-08 05:20 +0200
[PATCH 2/9] resource: add walk_system_ram_res_rev() AKASHI Takahiro <takahiro.akashi@linaro.org> - 2017-09-08 05:20 +0200
[PATCH 6/9] arm64: kexec_file: load initrd, device-tree and purgatory segments AKASHI Takahiro <takahiro.akashi@linaro.org> - 2017-09-08 05:20 +0200
[PATCH 7/9] arm64: kexec_file: set up for crash dump adding elf core header AKASHI Takahiro <takahiro.akashi@linaro.org> - 2017-09-08 05:20 +0200
[PATCH 1/9] include: pe.h: remove message[] from mz header definition AKASHI Takahiro <takahiro.akashi@linaro.org> - 2017-09-08 05:20 +0200
[PATCH 3/9] kexec_file: factor out crashdump elf header function from x86 AKASHI Takahiro <takahiro.akashi@linaro.org> - 2017-09-08 05:20 +0200
| From | AKASHI Takahiro <takahiro.akashi@linaro.org> |
|---|---|
| Date | 2017-09-08 05:20 +0200 |
| Subject | [PATCH 0/9] kexec: add kexec_file_load() support |
| Message-ID | <unhZL-18K-3@gated-at.bofh.it> |
This is the second round of implementing kexec_file_load() support
on arm64.[1]
Most of the code is based on kexec-tools (along with some kernel code
from x86, which also came from kexec-tools).
This patch series enables us to
* load the kernel, Image, with kexec_file_load system call, and
* optionally verify its signature at load time for trusted boot.
To load the kernel via kexec_file_load system call, a small change
is also needed to kexec-tools. See [2]. This enables '-s' option.
As we discussed a long time ago, users may not be allowed to specify
device-tree file of the 2nd kernel explicitly with kexec-tools, therefore
re-using the blob of the first kernel.
Regarding a signing method, we conform with x86 (or rather Microsoft?)
style of signing since the binary can also be seen as in PE format
(assuming that CONFIG_EFI is enabled).
Powerpc is also going to support extended-file-attribute-based
verification[3] with vmlinux, but arm64 doesn't for now partly
because we don't have TPM-based IMA at this moment.
Accordingly, we can use the existing command, sbsign, to sign the kernel.
$ sbsign --key ${KEY} --cert ${CERT} Image
Please note that it is totally up to the system what key/certificate is
used for signing, but one of easy ways to *try* this feature is to turn on
CONFIG_MODULE_SIG so that we can reuse certs/signing_key.pem as a signing
key, KEY and CERT above, for kernel.
(This also enables CONFIG_CRYPTO_SHA1 by default.)
Some concerns(or future works):
* Even if the kernel is configured with CONFIG_RANDOMIZE_BASE, the 2nd
kernel won't be placed at a randomized address. We will have to
add some boot code similar to efi-stub to implement the feature.
* While big-endian kernel can support kernel signing, I'm not sure that
Image can be recognized as in PE format because x86 standard only
defines little-endian-based format.
* IMA(and file extended attribute)-based kexec
* vmlinux support
[1] http://git.linaro.org/people/takahiro.akashi/linux-aarch64.git
branch:arm64/kexec_file
[2] http://git.linaro.org/people/takahiro.akashi/kexec-tools.git
branch:arm64/kexec_file
[3] http://lkml.iu.edu//hypermail/linux/kernel/1707.0/03669.html
Changes in v2 (Sep 8, 2017)
* move core-header-related functions from crash_core.c to kexec_file.c
* drop hash-check code from purgatory
* modify purgatory asm to remove arch_kexec_apply_relocations_add()
* drop older kernel support
* drop vmlinux support (at least, for this series)
Patch #1 to #4 are all preparatory patches on generic side.
Patch #5 is purgatory code.
Patch #6 to #8 are common for enabling kexec_file_load.
Patch #9 is for 'Image' support.
AKASHI Takahiro (9):
include: pe.h: remove message[] from mz header definition
resource: add walk_system_ram_res_rev()
kexec_file: factor out crashdump elf header function from x86
asm-generic: add kexec_file_load system call to unistd.h
arm64: kexec_file: create purgatory
arm64: kexec_file: load initrd, device-tree and purgatory segments
arm64: kexec_file: set up for crash dump adding elf core header
arm64: enable KEXEC_FILE config
arm64: kexec_file: add Image format support
arch/arm64/Kconfig | 29 +++
arch/arm64/Makefile | 1 +
arch/arm64/include/asm/kexec.h | 24 ++
arch/arm64/include/asm/kexec_file.h | 69 ++++++
arch/arm64/kernel/Makefile | 4 +-
arch/arm64/kernel/kexec_image.c | 106 +++++++++
arch/arm64/kernel/machine_kexec_file.c | 413 +++++++++++++++++++++++++++++++++
arch/arm64/purgatory/Makefile | 24 ++
arch/arm64/purgatory/entry.S | 55 +++++
arch/x86/kernel/crash.c | 324 --------------------------
include/linux/ioport.h | 3 +
include/linux/kexec.h | 19 ++
include/linux/pe.h | 2 +-
include/uapi/asm-generic/unistd.h | 4 +-
kernel/kexec_file.c | 329 ++++++++++++++++++++++++++
kernel/resource.c | 59 +++++
16 files changed, 1138 insertions(+), 327 deletions(-)
create mode 100644 arch/arm64/include/asm/kexec_file.h
create mode 100644 arch/arm64/kernel/kexec_image.c
create mode 100644 arch/arm64/kernel/machine_kexec_file.c
create mode 100644 arch/arm64/purgatory/Makefile
create mode 100644 arch/arm64/purgatory/entry.S
--
2.14.1
[toc] | [next] | [standalone]
| From | AKASHI Takahiro <takahiro.akashi@linaro.org> |
|---|---|
| Date | 2017-09-08 05:20 +0200 |
| Subject | [PATCH 2/9] resource: add walk_system_ram_res_rev() |
| Message-ID | <unhZN-18K-15@gated-at.bofh.it> |
| In reply to | #1728563 |
This function, being a variant of walk_system_ram_res() introduced in
commit 8c86e70acead ("resource: provide new functions to walk through
resources"), walks through a list of all the resources of System RAM
in reversed order, i.e., from higher to lower.
It will be used in kexec_file implementation on arm64.
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Cc: Vivek Goyal <vgoyal@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
---
include/linux/ioport.h | 3 +++
kernel/resource.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 62 insertions(+)
diff --git a/include/linux/ioport.h b/include/linux/ioport.h
index 6230064d7f95..9a212266299f 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -271,6 +271,9 @@ extern int
walk_system_ram_res(u64 start, u64 end, void *arg,
int (*func)(u64, u64, void *));
extern int
+walk_system_ram_res_rev(u64 start, u64 end, void *arg,
+ int (*func)(u64, u64, void *));
+extern int
walk_iomem_res_desc(unsigned long desc, unsigned long flags, u64 start, u64 end,
void *arg, int (*func)(u64, u64, void *));
diff --git a/kernel/resource.c b/kernel/resource.c
index 9b5f04404152..572f2f91ce9c 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -23,6 +23,8 @@
#include <linux/pfn.h>
#include <linux/mm.h>
#include <linux/resource_ext.h>
+#include <linux/string.h>
+#include <linux/vmalloc.h>
#include <asm/io.h>
@@ -469,6 +471,63 @@ int walk_system_ram_res(u64 start, u64 end, void *arg,
return ret;
}
+int walk_system_ram_res_rev(u64 start, u64 end, void *arg,
+ int (*func)(u64, u64, void *))
+{
+ struct resource res, *rams;
+ u64 orig_end;
+ int count, i;
+ int ret = -1;
+
+ count = 16; /* initial */
+
+ /* create a list */
+ rams = vmalloc(sizeof(struct resource) * count);
+ if (!rams)
+ return ret;
+
+ res.start = start;
+ res.end = end;
+ res.flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY;
+ orig_end = res.end;
+ i = 0;
+ while ((res.start < res.end) &&
+ (!find_next_iomem_res(&res, IORES_DESC_NONE, true))) {
+ if (i >= count) {
+ /* re-alloc */
+ struct resource *rams_new;
+ int count_new;
+
+ count_new = count + 16;
+ rams_new = vmalloc(sizeof(struct resource) * count_new);
+ if (!rams_new)
+ goto out;
+
+ memcpy(rams_new, rams, count);
+ vfree(rams);
+ rams = rams_new;
+ count = count_new;
+ }
+
+ rams[i].start = res.start;
+ rams[i++].end = res.end;
+
+ res.start = res.end + 1;
+ res.end = orig_end;
+ }
+
+ /* go reverse */
+ for (i--; i >= 0; i--) {
+ ret = (*func)(rams[i].start, rams[i].end, arg);
+ if (ret)
+ break;
+ }
+
+out:
+ vfree(rams);
+ return ret;
+}
+
#if !defined(CONFIG_ARCH_HAS_WALK_MEMORY)
/*
--
2.14.1
[toc] | [prev] | [next] | [standalone]
| From | AKASHI Takahiro <takahiro.akashi@linaro.org> |
|---|---|
| Date | 2017-09-08 05:20 +0200 |
| Subject | [PATCH 6/9] arm64: kexec_file: load initrd, device-tree and purgatory segments |
| Message-ID | <unhZM-18K-11@gated-at.bofh.it> |
| In reply to | #1728563 |
load_other_segments() sets up and adds all the memory segments necessary
other than kernel, including initrd, device-tree blob and purgatory.
Most of the code was borrowed from kexec-tools' counterpart.
In addition, arch_kexec_image_probe(), arch_kexec_image_load() and
arch_kexec_kernel_verify_sig() are added as stubs for supporting multiple
types of kernel image formats.
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
---
arch/arm64/include/asm/kexec.h | 19 +++
arch/arm64/kernel/machine_kexec_file.c | 264 +++++++++++++++++++++++++++++++++
2 files changed, 283 insertions(+)
create mode 100644 arch/arm64/kernel/machine_kexec_file.c
diff --git a/arch/arm64/include/asm/kexec.h b/arch/arm64/include/asm/kexec.h
index e17f0529a882..d74dc49670ad 100644
--- a/arch/arm64/include/asm/kexec.h
+++ b/arch/arm64/include/asm/kexec.h
@@ -93,6 +93,25 @@ static inline void crash_prepare_suspend(void) {}
static inline void crash_post_resume(void) {}
#endif
+#ifdef CONFIG_KEXEC_FILE
+#define ARCH_HAS_KIMAGE_ARCH
+
+struct kimage_arch {
+ void *dtb_buf;
+};
+
+struct kimage;
+
+extern int setup_dtb(struct kimage *image,
+ unsigned long initrd_load_addr, unsigned long initrd_len,
+ char *cmdline, unsigned long cmdline_len,
+ char **dtb_buf, size_t *dtb_buf_len);
+extern int load_other_segments(struct kimage *image,
+ unsigned long kernel_load_addr,
+ char *initrd, unsigned long initrd_len,
+ char *cmdline, unsigned long cmdline_len);
+#endif
+
#endif /* __ASSEMBLY__ */
#endif
diff --git a/arch/arm64/kernel/machine_kexec_file.c b/arch/arm64/kernel/machine_kexec_file.c
new file mode 100644
index 000000000000..a6d2c006b4f7
--- /dev/null
+++ b/arch/arm64/kernel/machine_kexec_file.c
@@ -0,0 +1,264 @@
+/*
+ * kexec_file for arm64
+ *
+ * Copyright (C) 2017 Linaro Limited
+ * Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+ *
+ * Most code is derived from arm64 port of kexec-tools
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) "kexec_file: " fmt
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/kexec.h>
+#include <linux/libfdt.h>
+#include <linux/memblock.h>
+#include <linux/of_fdt.h>
+#include <asm/kexec_file.h>
+
+static int __dt_root_addr_cells;
+static int __dt_root_size_cells;
+
+static struct kexec_file_ops *kexec_file_loaders[0];
+
+int arch_kexec_kernel_image_probe(struct kimage *image, void *buf,
+ unsigned long buf_len)
+{
+ struct kexec_file_ops *fops;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(kexec_file_loaders); i++) {
+ fops = kexec_file_loaders[i];
+ if (!fops || !fops->probe)
+ continue;
+
+ ret = fops->probe(buf, buf_len);
+ if (!ret) {
+ image->fops = fops;
+ return 0;
+ }
+ }
+
+ return -ENOEXEC;
+}
+
+void *arch_kexec_kernel_image_load(struct kimage *image)
+{
+ if (!image->fops || !image->fops->load)
+ return ERR_PTR(-ENOEXEC);
+
+ return image->fops->load(image, image->kernel_buf,
+ image->kernel_buf_len, image->initrd_buf,
+ image->initrd_buf_len, image->cmdline_buf,
+ image->cmdline_buf_len);
+}
+
+int arch_kimage_file_post_load_cleanup(struct kimage *image)
+{
+ vfree(image->arch.dtb_buf);
+ image->arch.dtb_buf = NULL;
+
+ vfree(image->arch.elf_headers);
+ image->arch.elf_headers = NULL;
+ image->arch.elf_headers_sz = 0;
+
+ if (!image->fops || !image->fops->cleanup)
+ return 0;
+
+ return image->fops->cleanup(image->image_loader_data);
+}
+
+#ifdef CONFIG_KEXEC_VERIFY_SIG
+int arch_kexec_kernel_verify_sig(struct kimage *image, void *kernel,
+ unsigned long kernel_len)
+{
+ if (!image->fops || !image->fops->verify_sig) {
+ pr_debug("kernel loader does not support signature verification.\n");
+ return -EKEYREJECTED;
+ }
+
+ return image->fops->verify_sig(kernel, kernel_len);
+}
+#endif
+
+int arch_kexec_walk_mem(struct kexec_buf *kbuf, int (*func)(u64, u64, void *))
+{
+ if (kbuf->image->type == KEXEC_TYPE_CRASH)
+ return walk_iomem_res_desc(crashk_res.desc,
+ IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY,
+ crashk_res.start, crashk_res.end,
+ kbuf, func);
+ else if (kbuf->top_down)
+ return walk_system_ram_res_rev(0, ULONG_MAX, kbuf, func);
+ else
+ return walk_system_ram_res(0, ULONG_MAX, kbuf, func);
+}
+
+int setup_dtb(struct kimage *image,
+ unsigned long initrd_load_addr, unsigned long initrd_len,
+ char *cmdline, unsigned long cmdline_len,
+ char **dtb_buf, size_t *dtb_buf_len)
+{
+ char *buf = NULL;
+ size_t buf_size;
+ int nodeoffset;
+ u64 value;
+ int range_len;
+ int ret;
+
+ /* duplicate dt blob */
+ buf_size = fdt_totalsize(initial_boot_params);
+ range_len = (__dt_root_addr_cells + __dt_root_size_cells) * sizeof(u32);
+
+ if (initrd_load_addr)
+ buf_size += fdt_prop_len("initrd-start", sizeof(u64))
+ + fdt_prop_len("initrd-end", sizeof(u64));
+
+ if (cmdline)
+ buf_size += fdt_prop_len("bootargs", cmdline_len + 1);
+
+ buf = vmalloc(buf_size);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto out_err;
+ }
+
+ ret = fdt_open_into(initial_boot_params, buf, buf_size);
+ if (ret)
+ goto out_err;
+
+ nodeoffset = fdt_path_offset(buf, "/chosen");
+ if (nodeoffset < 0)
+ goto out_err;
+
+ /* add bootargs */
+ if (cmdline) {
+ ret = fdt_setprop(buf, nodeoffset, "bootargs",
+ cmdline, cmdline_len + 1);
+ if (ret)
+ goto out_err;
+ }
+
+ /* add initrd-* */
+ if (initrd_load_addr) {
+ value = cpu_to_fdt64(initrd_load_addr);
+ ret = fdt_setprop(buf, nodeoffset, "initrd-start",
+ &value, sizeof(value));
+ if (ret)
+ goto out_err;
+
+ value = cpu_to_fdt64(initrd_load_addr + initrd_len);
+ ret = fdt_setprop(buf, nodeoffset, "initrd-end",
+ &value, sizeof(value));
+ if (ret)
+ goto out_err;
+ }
+
+ /* trim a buffer */
+ fdt_pack(buf);
+ *dtb_buf = buf;
+ *dtb_buf_len = fdt_totalsize(buf);
+
+ return 0;
+
+out_err:
+ vfree(buf);
+ return ret;
+}
+
+int load_other_segments(struct kimage *image, unsigned long kernel_load_addr,
+ char *initrd, unsigned long initrd_len,
+ char *cmdline, unsigned long cmdline_len)
+{
+ struct kexec_buf kbuf;
+ unsigned long initrd_load_addr = 0;
+ unsigned long purgatory_load_addr, dtb_load_addr;
+ char *dtb = NULL;
+ unsigned long dtb_len;
+ int ret = 0;
+
+ kbuf.image = image;
+ /* not allocate anything below the kernel */
+ kbuf.buf_min = kernel_load_addr;
+
+ /* Load initrd */
+ if (initrd) {
+ kbuf.buffer = initrd;
+ kbuf.bufsz = initrd_len;
+ kbuf.memsz = initrd_len;
+ kbuf.buf_align = PAGE_SIZE;
+ /* within 1GB-aligned window of up to 32GB in size */
+ kbuf.buf_max = round_down(kernel_load_addr, SZ_1G)
+ + (unsigned long)SZ_1G * 31;
+ kbuf.top_down = 0;
+
+ ret = kexec_add_buffer(&kbuf);
+ if (ret)
+ goto out_err;
+ initrd_load_addr = kbuf.mem;
+
+ pr_debug("Loaded initrd at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
+ initrd_load_addr, initrd_len, initrd_len);
+ }
+
+ /* Load dtb blob */
+ ret = setup_dtb(image, initrd_load_addr, initrd_len,
+ cmdline, cmdline_len, &dtb, &dtb_len);
+ if (ret) {
+ pr_err("Preparing for new dtb failed\n");
+ goto out_err;
+ }
+
+ kbuf.buffer = dtb;
+ kbuf.bufsz = dtb_len;
+ kbuf.memsz = dtb_len;
+ /* not across 2MB boundary */
+ kbuf.buf_align = SZ_2M;
+ kbuf.buf_max = ULONG_MAX;
+ kbuf.top_down = 1;
+
+ ret = kexec_add_buffer(&kbuf);
+ if (ret)
+ goto out_err;
+ dtb_load_addr = kbuf.mem;
+ image->arch.dtb_buf = dtb;
+
+ pr_debug("Loaded dtb at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
+ dtb_load_addr, dtb_len, dtb_len);
+
+ /* Load purgatory */
+ ret = kexec_load_purgatory(image, kernel_load_addr, ULONG_MAX, 1,
+ &purgatory_load_addr);
+ if (ret) {
+ pr_err("Loading purgatory failed\n");
+ goto out_err;
+ }
+
+ ret = kexec_purgatory_get_set_symbol(image, "kernel_entry",
+ &kernel_load_addr, sizeof(kernel_load_addr), 0);
+ if (ret) {
+ pr_err("Setting symbol (kernel_entry) failed.\n");
+ goto out_err;
+ }
+
+ ret = kexec_purgatory_get_set_symbol(image, "dtb_addr",
+ &dtb_load_addr, sizeof(dtb_load_addr), 0);
+ if (ret) {
+ pr_err("Setting symbol (dtb_addr) failed.\n");
+ goto out_err;
+ }
+
+ pr_debug("Loaded purgatory at 0x%lx\n", purgatory_load_addr);
+
+ return 0;
+
+out_err:
+ vfree(dtb);
+ image->arch.dtb_buf = NULL;
+ return ret;
+}
--
2.14.1
[toc] | [prev] | [next] | [standalone]
| From | AKASHI Takahiro <takahiro.akashi@linaro.org> |
|---|---|
| Date | 2017-09-08 05:20 +0200 |
| Subject | [PATCH 7/9] arm64: kexec_file: set up for crash dump adding elf core header |
| Message-ID | <unhZM-18K-13@gated-at.bofh.it> |
| In reply to | #1728563 |
load_crashdump_segments() creates and loads a memory segment of elf core
header for crash dump.
"linux,usable-memory-range" and "linux,elfcorehdr" will add to the 2nd
kernel's device-tree blob. The logic of this cod is also from kexec-tools.
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
---
arch/arm64/include/asm/kexec.h | 5 ++
arch/arm64/kernel/machine_kexec_file.c | 145 +++++++++++++++++++++++++++++++++
2 files changed, 150 insertions(+)
diff --git a/arch/arm64/include/asm/kexec.h b/arch/arm64/include/asm/kexec.h
index d74dc49670ad..66537de588fa 100644
--- a/arch/arm64/include/asm/kexec.h
+++ b/arch/arm64/include/asm/kexec.h
@@ -98,6 +98,10 @@ static inline void crash_post_resume(void) {}
struct kimage_arch {
void *dtb_buf;
+ /* Core ELF header buffer */
+ void *elf_headers;
+ unsigned long elf_headers_sz;
+ unsigned long elf_load_addr;
};
struct kimage;
@@ -110,6 +114,7 @@ extern int load_other_segments(struct kimage *image,
unsigned long kernel_load_addr,
char *initrd, unsigned long initrd_len,
char *cmdline, unsigned long cmdline_len);
+extern int load_crashdump_segments(struct kimage *image);
#endif
#endif /* __ASSEMBLY__ */
diff --git a/arch/arm64/kernel/machine_kexec_file.c b/arch/arm64/kernel/machine_kexec_file.c
index a6d2c006b4f7..62627f5440ff 100644
--- a/arch/arm64/kernel/machine_kexec_file.c
+++ b/arch/arm64/kernel/machine_kexec_file.c
@@ -99,6 +99,77 @@ int arch_kexec_walk_mem(struct kexec_buf *kbuf, int (*func)(u64, u64, void *))
return walk_system_ram_res(0, ULONG_MAX, kbuf, func);
}
+static int __init arch_kexec_file_init(void)
+{
+ /* Those values are used later on loading the kernel */
+ __dt_root_addr_cells = dt_root_addr_cells;
+ __dt_root_size_cells = dt_root_size_cells;
+
+ return 0;
+}
+late_initcall(arch_kexec_file_init);
+
+#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
+#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE))
+
+static int fdt_prop_len(const char *prop_name, int len)
+{
+ return (strlen(prop_name) + 1) +
+ sizeof(struct fdt_property) +
+ FDT_TAGALIGN(len);
+}
+
+static bool cells_size_fitted(unsigned long base, unsigned long size)
+{
+ /* if *_cells >= 2, cells can hold 64-bit values anyway */
+ if ((__dt_root_addr_cells == 1) && (base >= (1ULL << 32)))
+ return false;
+
+ if ((__dt_root_size_cells == 1) && (size >= (1ULL << 32)))
+ return false;
+
+ return true;
+}
+
+static void fill_property(void *buf, u64 val64, int cells)
+{
+ u32 val32;
+ int i;
+
+ if (cells == 1) {
+ val32 = cpu_to_fdt32((u32)val64);
+ memcpy(buf, &val32, sizeof(val32));
+ } else {
+ for (i = 0; i < (cells * sizeof(u32) - sizeof(u64)); i++)
+ *(char *)buf++ = 0;
+
+ val64 = cpu_to_fdt64(val64);
+ memcpy(buf, &val64, sizeof(val64));
+ }
+}
+
+static int fdt_setprop_range(void *fdt, int nodeoffset, const char *name,
+ unsigned long addr, unsigned long size)
+{
+ u64 range[2];
+ void *prop;
+ size_t buf_size;
+ int result;
+
+ prop = range;
+ buf_size = (__dt_root_addr_cells + __dt_root_size_cells) * sizeof(u32);
+
+ fill_property(prop, addr, __dt_root_addr_cells);
+ prop += __dt_root_addr_cells * sizeof(u32);
+
+ fill_property(prop, size, __dt_root_size_cells);
+ prop += __dt_root_size_cells * sizeof(u32);
+
+ result = fdt_setprop(fdt, nodeoffset, name, range, buf_size);
+
+ return result;
+}
+
int setup_dtb(struct kimage *image,
unsigned long initrd_load_addr, unsigned long initrd_len,
char *cmdline, unsigned long cmdline_len,
@@ -111,10 +182,26 @@ int setup_dtb(struct kimage *image,
int range_len;
int ret;
+ /* check ranges against root's #address-cells and #size-cells */
+ if (image->type == KEXEC_TYPE_CRASH &&
+ (!cells_size_fitted(image->arch.elf_load_addr,
+ image->arch.elf_headers_sz) ||
+ !cells_size_fitted(crashk_res.start,
+ crashk_res.end - crashk_res.start + 1))) {
+ pr_err("Crash memory region doesn't fit into DT's root cell sizes.\n");
+ ret = -EINVAL;
+ goto out_err;
+ }
+
/* duplicate dt blob */
buf_size = fdt_totalsize(initial_boot_params);
range_len = (__dt_root_addr_cells + __dt_root_size_cells) * sizeof(u32);
+ if (image->type == KEXEC_TYPE_CRASH)
+ buf_size += fdt_prop_len("linux,elfcorehdr", range_len)
+ + fdt_prop_len("linux,usable-memory-range",
+ range_len);
+
if (initrd_load_addr)
buf_size += fdt_prop_len("initrd-start", sizeof(u64))
+ fdt_prop_len("initrd-end", sizeof(u64));
@@ -136,6 +223,23 @@ int setup_dtb(struct kimage *image,
if (nodeoffset < 0)
goto out_err;
+ if (image->type == KEXEC_TYPE_CRASH) {
+ /* add linux,elfcorehdr */
+ ret = fdt_setprop_range(buf, nodeoffset, "linux,elfcorehdr",
+ image->arch.elf_load_addr,
+ image->arch.elf_headers_sz);
+ if (ret)
+ goto out_err;
+
+ /* add linux,usable-memory-range */
+ ret = fdt_setprop_range(buf, nodeoffset,
+ "linux,usable-memory-range",
+ crashk_res.start,
+ crashk_res.end - crashk_res.start + 1);
+ if (ret)
+ goto out_err;
+ }
+
/* add bootargs */
if (cmdline) {
ret = fdt_setprop(buf, nodeoffset, "bootargs",
@@ -262,3 +366,44 @@ int load_other_segments(struct kimage *image, unsigned long kernel_load_addr,
image->arch.dtb_buf = NULL;
return ret;
}
+
+int load_crashdump_segments(struct kimage *image)
+{
+ void *elf_addr;
+ unsigned long elf_sz;
+ struct kexec_buf kbuf;
+ int ret;
+
+ if (image->type != KEXEC_TYPE_CRASH)
+ return 0;
+
+ /* Prepare elf headers and add a segment */
+ ret = prepare_elf_headers(image, &elf_addr, &elf_sz);
+ if (ret) {
+ pr_err("Preparing elf core header failed\n");
+ return ret;
+ }
+
+ kbuf.image = image;
+ kbuf.buffer = elf_addr;
+ kbuf.bufsz = elf_sz;
+ kbuf.memsz = elf_sz;
+ kbuf.buf_align = PAGE_SIZE;
+ kbuf.buf_min = crashk_res.start;
+ kbuf.buf_max = crashk_res.end + 1;
+ kbuf.top_down = 1;
+
+ ret = kexec_add_buffer(&kbuf);
+ if (ret) {
+ vfree(elf_addr);
+ return ret;
+ }
+ image->arch.elf_headers = elf_addr;
+ image->arch.elf_headers_sz = elf_sz;
+ image->arch.elf_load_addr = kbuf.mem;
+
+ pr_debug("Loaded elf core header at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
+ image->arch.elf_load_addr, elf_sz, elf_sz);
+
+ return ret;
+}
--
2.14.1
[toc] | [prev] | [next] | [standalone]
| From | AKASHI Takahiro <takahiro.akashi@linaro.org> |
|---|---|
| Date | 2017-09-08 05:20 +0200 |
| Subject | [PATCH 1/9] include: pe.h: remove message[] from mz header definition |
| Message-ID | <unhZN-18K-19@gated-at.bofh.it> |
| In reply to | #1728563 |
message[] field won't be part of the definition of mz header.
This change is crucial for enabling kexec_file_load on arm64 because
arm64's "Image" binary, as in PE format, doesn't have any data for it and
accordingly the following check in pefile_parse_binary() will fail:
chkaddr(cursor, mz->peaddr, sizeof(*pe));
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: David Howells <dhowells@redhat.com>
Cc: Vivek Goyal <vgoyal@redhat.com>
Cc: Herbert Xu <herbert@gondor.apana.org.au>
Cc: David S. Miller <davem@davemloft.net>
---
include/linux/pe.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/linux/pe.h b/include/linux/pe.h
index 143ce75be5f0..3482b18a48b5 100644
--- a/include/linux/pe.h
+++ b/include/linux/pe.h
@@ -166,7 +166,7 @@ struct mz_hdr {
uint16_t oem_info; /* oem specific */
uint16_t reserved1[10]; /* reserved */
uint32_t peaddr; /* address of pe header */
- char message[64]; /* message to print */
+ char message[]; /* message to print */
};
struct mz_reloc {
--
2.14.1
[toc] | [prev] | [next] | [standalone]
| From | AKASHI Takahiro <takahiro.akashi@linaro.org> |
|---|---|
| Date | 2017-09-08 05:20 +0200 |
| Subject | [PATCH 3/9] kexec_file: factor out crashdump elf header function from x86 |
| Message-ID | <unhZN-18K-23@gated-at.bofh.it> |
| In reply to | #1728563 |
prepare_elf_headers() can also be useful for other architectures,
including arm64. So let it factored out.
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Cc: Dave Young <dyoung@redhat.com>
Cc: Vivek Goyal <vgoyal@redhat.com>
Cc: Baoquan He <bhe@redhat.com>
---
arch/x86/kernel/crash.c | 324 -----------------------------------------------
include/linux/kexec.h | 19 +++
kernel/kexec_file.c | 329 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 348 insertions(+), 324 deletions(-)
diff --git a/arch/x86/kernel/crash.c b/arch/x86/kernel/crash.c
index 44404e2307bb..3c6b880f6dbf 100644
--- a/arch/x86/kernel/crash.c
+++ b/arch/x86/kernel/crash.c
@@ -21,7 +21,6 @@
#include <linux/elf.h>
#include <linux/elfcore.h>
#include <linux/export.h>
-#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <asm/processor.h>
@@ -41,34 +40,6 @@
/* Alignment required for elf header segment */
#define ELF_CORE_HEADER_ALIGN 4096
-/* This primarily represents number of split ranges due to exclusion */
-#define CRASH_MAX_RANGES 16
-
-struct crash_mem_range {
- u64 start, end;
-};
-
-struct crash_mem {
- unsigned int nr_ranges;
- struct crash_mem_range ranges[CRASH_MAX_RANGES];
-};
-
-/* Misc data about ram ranges needed to prepare elf headers */
-struct crash_elf_data {
- struct kimage *image;
- /*
- * Total number of ram ranges we have after various adjustments for
- * crash reserved region, etc.
- */
- unsigned int max_nr_ranges;
-
- /* Pointer to elf header */
- void *ehdr;
- /* Pointer to next phdr */
- void *bufp;
- struct crash_mem mem;
-};
-
/* Used while preparing memory map entries for second kernel */
struct crash_memmap_data {
struct boot_params *params;
@@ -209,301 +180,6 @@ void native_machine_crash_shutdown(struct pt_regs *regs)
}
#ifdef CONFIG_KEXEC_FILE
-static int get_nr_ram_ranges_callback(u64 start, u64 end, void *arg)
-{
- unsigned int *nr_ranges = arg;
-
- (*nr_ranges)++;
- return 0;
-}
-
-
-/* Gather all the required information to prepare elf headers for ram regions */
-static void fill_up_crash_elf_data(struct crash_elf_data *ced,
- struct kimage *image)
-{
- unsigned int nr_ranges = 0;
-
- ced->image = image;
-
- walk_system_ram_res(0, -1, &nr_ranges,
- get_nr_ram_ranges_callback);
-
- ced->max_nr_ranges = nr_ranges;
-
- /* Exclusion of crash region could split memory ranges */
- ced->max_nr_ranges++;
-
- /* If crashk_low_res is not 0, another range split possible */
- if (crashk_low_res.end)
- ced->max_nr_ranges++;
-}
-
-static int exclude_mem_range(struct crash_mem *mem,
- unsigned long long mstart, unsigned long long mend)
-{
- int i, j;
- unsigned long long start, end;
- struct crash_mem_range temp_range = {0, 0};
-
- for (i = 0; i < mem->nr_ranges; i++) {
- start = mem->ranges[i].start;
- end = mem->ranges[i].end;
-
- if (mstart > end || mend < start)
- continue;
-
- /* Truncate any area outside of range */
- if (mstart < start)
- mstart = start;
- if (mend > end)
- mend = end;
-
- /* Found completely overlapping range */
- if (mstart == start && mend == end) {
- mem->ranges[i].start = 0;
- mem->ranges[i].end = 0;
- if (i < mem->nr_ranges - 1) {
- /* Shift rest of the ranges to left */
- for (j = i; j < mem->nr_ranges - 1; j++) {
- mem->ranges[j].start =
- mem->ranges[j+1].start;
- mem->ranges[j].end =
- mem->ranges[j+1].end;
- }
- }
- mem->nr_ranges--;
- return 0;
- }
-
- if (mstart > start && mend < end) {
- /* Split original range */
- mem->ranges[i].end = mstart - 1;
- temp_range.start = mend + 1;
- temp_range.end = end;
- } else if (mstart != start)
- mem->ranges[i].end = mstart - 1;
- else
- mem->ranges[i].start = mend + 1;
- break;
- }
-
- /* If a split happend, add the split to array */
- if (!temp_range.end)
- return 0;
-
- /* Split happened */
- if (i == CRASH_MAX_RANGES - 1) {
- pr_err("Too many crash ranges after split\n");
- return -ENOMEM;
- }
-
- /* Location where new range should go */
- j = i + 1;
- if (j < mem->nr_ranges) {
- /* Move over all ranges one slot towards the end */
- for (i = mem->nr_ranges - 1; i >= j; i--)
- mem->ranges[i + 1] = mem->ranges[i];
- }
-
- mem->ranges[j].start = temp_range.start;
- mem->ranges[j].end = temp_range.end;
- mem->nr_ranges++;
- return 0;
-}
-
-/*
- * Look for any unwanted ranges between mstart, mend and remove them. This
- * might lead to split and split ranges are put in ced->mem.ranges[] array
- */
-static int elf_header_exclude_ranges(struct crash_elf_data *ced,
- unsigned long long mstart, unsigned long long mend)
-{
- struct crash_mem *cmem = &ced->mem;
- int ret = 0;
-
- memset(cmem->ranges, 0, sizeof(cmem->ranges));
-
- cmem->ranges[0].start = mstart;
- cmem->ranges[0].end = mend;
- cmem->nr_ranges = 1;
-
- /* Exclude crashkernel region */
- ret = exclude_mem_range(cmem, crashk_res.start, crashk_res.end);
- if (ret)
- return ret;
-
- if (crashk_low_res.end) {
- ret = exclude_mem_range(cmem, crashk_low_res.start, crashk_low_res.end);
- if (ret)
- return ret;
- }
-
- return ret;
-}
-
-static int prepare_elf64_ram_headers_callback(u64 start, u64 end, void *arg)
-{
- struct crash_elf_data *ced = arg;
- Elf64_Ehdr *ehdr;
- Elf64_Phdr *phdr;
- unsigned long mstart, mend;
- struct kimage *image = ced->image;
- struct crash_mem *cmem;
- int ret, i;
-
- ehdr = ced->ehdr;
-
- /* Exclude unwanted mem ranges */
- ret = elf_header_exclude_ranges(ced, start, end);
- if (ret)
- return ret;
-
- /* Go through all the ranges in ced->mem.ranges[] and prepare phdr */
- cmem = &ced->mem;
-
- for (i = 0; i < cmem->nr_ranges; i++) {
- mstart = cmem->ranges[i].start;
- mend = cmem->ranges[i].end;
-
- phdr = ced->bufp;
- ced->bufp += sizeof(Elf64_Phdr);
-
- phdr->p_type = PT_LOAD;
- phdr->p_flags = PF_R|PF_W|PF_X;
- phdr->p_offset = mstart;
-
- /*
- * If a range matches backup region, adjust offset to backup
- * segment.
- */
- if (mstart == image->arch.backup_src_start &&
- (mend - mstart + 1) == image->arch.backup_src_sz)
- phdr->p_offset = image->arch.backup_load_addr;
-
- phdr->p_paddr = mstart;
- phdr->p_vaddr = (unsigned long long) __va(mstart);
- phdr->p_filesz = phdr->p_memsz = mend - mstart + 1;
- phdr->p_align = 0;
- ehdr->e_phnum++;
- pr_debug("Crash PT_LOAD elf header. phdr=%p vaddr=0x%llx, paddr=0x%llx, sz=0x%llx e_phnum=%d p_offset=0x%llx\n",
- phdr, phdr->p_vaddr, phdr->p_paddr, phdr->p_filesz,
- ehdr->e_phnum, phdr->p_offset);
- }
-
- return ret;
-}
-
-static int prepare_elf64_headers(struct crash_elf_data *ced,
- void **addr, unsigned long *sz)
-{
- Elf64_Ehdr *ehdr;
- Elf64_Phdr *phdr;
- unsigned long nr_cpus = num_possible_cpus(), nr_phdr, elf_sz;
- unsigned char *buf, *bufp;
- unsigned int cpu;
- unsigned long long notes_addr;
- int ret;
-
- /* extra phdr for vmcoreinfo elf note */
- nr_phdr = nr_cpus + 1;
- nr_phdr += ced->max_nr_ranges;
-
- /*
- * kexec-tools creates an extra PT_LOAD phdr for kernel text mapping
- * area on x86_64 (ffffffff80000000 - ffffffffa0000000).
- * I think this is required by tools like gdb. So same physical
- * memory will be mapped in two elf headers. One will contain kernel
- * text virtual addresses and other will have __va(physical) addresses.
- */
-
- nr_phdr++;
- elf_sz = sizeof(Elf64_Ehdr) + nr_phdr * sizeof(Elf64_Phdr);
- elf_sz = ALIGN(elf_sz, ELF_CORE_HEADER_ALIGN);
-
- buf = vzalloc(elf_sz);
- if (!buf)
- return -ENOMEM;
-
- bufp = buf;
- ehdr = (Elf64_Ehdr *)bufp;
- bufp += sizeof(Elf64_Ehdr);
- memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
- ehdr->e_ident[EI_CLASS] = ELFCLASS64;
- ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
- ehdr->e_ident[EI_VERSION] = EV_CURRENT;
- ehdr->e_ident[EI_OSABI] = ELF_OSABI;
- memset(ehdr->e_ident + EI_PAD, 0, EI_NIDENT - EI_PAD);
- ehdr->e_type = ET_CORE;
- ehdr->e_machine = ELF_ARCH;
- ehdr->e_version = EV_CURRENT;
- ehdr->e_phoff = sizeof(Elf64_Ehdr);
- ehdr->e_ehsize = sizeof(Elf64_Ehdr);
- ehdr->e_phentsize = sizeof(Elf64_Phdr);
-
- /* Prepare one phdr of type PT_NOTE for each present cpu */
- for_each_present_cpu(cpu) {
- phdr = (Elf64_Phdr *)bufp;
- bufp += sizeof(Elf64_Phdr);
- phdr->p_type = PT_NOTE;
- notes_addr = per_cpu_ptr_to_phys(per_cpu_ptr(crash_notes, cpu));
- phdr->p_offset = phdr->p_paddr = notes_addr;
- phdr->p_filesz = phdr->p_memsz = sizeof(note_buf_t);
- (ehdr->e_phnum)++;
- }
-
- /* Prepare one PT_NOTE header for vmcoreinfo */
- phdr = (Elf64_Phdr *)bufp;
- bufp += sizeof(Elf64_Phdr);
- phdr->p_type = PT_NOTE;
- phdr->p_offset = phdr->p_paddr = paddr_vmcoreinfo_note();
- phdr->p_filesz = phdr->p_memsz = VMCOREINFO_NOTE_SIZE;
- (ehdr->e_phnum)++;
-
-#ifdef CONFIG_X86_64
- /* Prepare PT_LOAD type program header for kernel text region */
- phdr = (Elf64_Phdr *)bufp;
- bufp += sizeof(Elf64_Phdr);
- phdr->p_type = PT_LOAD;
- phdr->p_flags = PF_R|PF_W|PF_X;
- phdr->p_vaddr = (Elf64_Addr)_text;
- phdr->p_filesz = phdr->p_memsz = _end - _text;
- phdr->p_offset = phdr->p_paddr = __pa_symbol(_text);
- (ehdr->e_phnum)++;
-#endif
-
- /* Prepare PT_LOAD headers for system ram chunks. */
- ced->ehdr = ehdr;
- ced->bufp = bufp;
- ret = walk_system_ram_res(0, -1, ced,
- prepare_elf64_ram_headers_callback);
- if (ret < 0)
- return ret;
-
- *addr = buf;
- *sz = elf_sz;
- return 0;
-}
-
-/* Prepare elf headers. Return addr and size */
-static int prepare_elf_headers(struct kimage *image, void **addr,
- unsigned long *sz)
-{
- struct crash_elf_data *ced;
- int ret;
-
- ced = kzalloc(sizeof(*ced), GFP_KERNEL);
- if (!ced)
- return -ENOMEM;
-
- fill_up_crash_elf_data(ced, image);
-
- /* By default prepare 64bit headers */
- ret = prepare_elf64_headers(ced, addr, sz);
- kfree(ced);
- return ret;
-}
-
static int add_e820_entry(struct boot_params *params, struct e820_entry *entry)
{
unsigned int nr_e820_entries;
diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index dd056fab9e35..65897af3125c 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -162,6 +162,25 @@ int __weak arch_kexec_walk_mem(struct kexec_buf *kbuf,
int (*func)(u64, u64, void *));
extern int kexec_add_buffer(struct kexec_buf *kbuf);
int kexec_locate_mem_hole(struct kexec_buf *kbuf);
+#ifdef CONFIG_CRASH_CORE
+extern int prepare_elf_headers(struct kimage *image, void **addr,
+ unsigned long *sz);
+
+/* This primarily represents number of split ranges due to exclusion */
+#define CRASH_MAX_RANGES 16
+
+struct crash_mem_range {
+ u64 start, end;
+};
+
+struct crash_mem {
+ unsigned int nr_ranges;
+ struct crash_mem_range ranges[CRASH_MAX_RANGES];
+};
+
+extern int exclude_mem_range(struct crash_mem *mem,
+ unsigned long long mstart, unsigned long long mend);
+#endif /* CONFIG_CRASH_CORE */
#endif /* CONFIG_KEXEC_FILE */
struct kimage {
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index 9f48f4412297..20565eae6bd5 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -22,6 +22,11 @@
#include <linux/ima.h>
#include <crypto/hash.h>
#include <crypto/sha.h>
+#include <linux/elf.h>
+#include <linux/elfcore.h>
+#include <linux/kernel.h>
+#include <linux/kexec.h>
+#include <linux/slab.h>
#include <linux/syscalls.h>
#include <linux/vmalloc.h>
#include "kexec_internal.h"
@@ -1021,3 +1026,327 @@ int kexec_purgatory_get_set_symbol(struct kimage *image, const char *name,
return 0;
}
+
+#ifdef CONFIG_CRASH_CORE
+/* Alignment required for elf header segment */
+#define ELF_CORE_HEADER_ALIGN 4096
+
+/* Misc data about ram ranges needed to prepare elf headers */
+struct crash_elf_data {
+ struct kimage *image;
+ /*
+ * Total number of ram ranges we have after various adjustments for
+ * crash reserved region, etc.
+ */
+ unsigned int max_nr_ranges;
+
+ /* Pointer to elf header */
+ void *ehdr;
+ /* Pointer to next phdr */
+ void *bufp;
+ struct crash_mem mem;
+};
+
+static int get_nr_ram_ranges_callback(u64 start, u64 end, void *arg)
+{
+ unsigned int *nr_ranges = arg;
+
+ (*nr_ranges)++;
+ return 0;
+}
+
+
+/* Gather all the required information to prepare elf headers for ram regions */
+static void fill_up_crash_elf_data(struct crash_elf_data *ced,
+ struct kimage *image)
+{
+ unsigned int nr_ranges = 0;
+
+ ced->image = image;
+
+ walk_system_ram_res(0, -1, &nr_ranges,
+ get_nr_ram_ranges_callback);
+
+ ced->max_nr_ranges = nr_ranges;
+
+ /* Exclusion of crash region could split memory ranges */
+ ced->max_nr_ranges++;
+
+#ifdef CONFIG_X86_64
+ /* If crashk_low_res is not 0, another range split possible */
+ if (crashk_low_res.end)
+ ced->max_nr_ranges++;
+#endif
+}
+
+int exclude_mem_range(struct crash_mem *mem,
+ unsigned long long mstart, unsigned long long mend)
+{
+ int i, j;
+ unsigned long long start, end;
+ struct crash_mem_range temp_range = {0, 0};
+
+ for (i = 0; i < mem->nr_ranges; i++) {
+ start = mem->ranges[i].start;
+ end = mem->ranges[i].end;
+
+ if (mstart > end || mend < start)
+ continue;
+
+ /* Truncate any area outside of range */
+ if (mstart < start)
+ mstart = start;
+ if (mend > end)
+ mend = end;
+
+ /* Found completely overlapping range */
+ if (mstart == start && mend == end) {
+ mem->ranges[i].start = 0;
+ mem->ranges[i].end = 0;
+ if (i < mem->nr_ranges - 1) {
+ /* Shift rest of the ranges to left */
+ for (j = i; j < mem->nr_ranges - 1; j++) {
+ mem->ranges[j].start =
+ mem->ranges[j+1].start;
+ mem->ranges[j].end =
+ mem->ranges[j+1].end;
+ }
+ }
+ mem->nr_ranges--;
+ return 0;
+ }
+
+ if (mstart > start && mend < end) {
+ /* Split original range */
+ mem->ranges[i].end = mstart - 1;
+ temp_range.start = mend + 1;
+ temp_range.end = end;
+ } else if (mstart != start)
+ mem->ranges[i].end = mstart - 1;
+ else
+ mem->ranges[i].start = mend + 1;
+ break;
+ }
+
+ /* If a split happened, add the split to array */
+ if (!temp_range.end)
+ return 0;
+
+ /* Split happened */
+ if (i == CRASH_MAX_RANGES - 1) {
+ pr_err("Too many crash ranges after split\n");
+ return -ENOMEM;
+ }
+
+ /* Location where new range should go */
+ j = i + 1;
+ if (j < mem->nr_ranges) {
+ /* Move over all ranges one slot towards the end */
+ for (i = mem->nr_ranges - 1; i >= j; i--)
+ mem->ranges[i + 1] = mem->ranges[i];
+ }
+
+ mem->ranges[j].start = temp_range.start;
+ mem->ranges[j].end = temp_range.end;
+ mem->nr_ranges++;
+ return 0;
+}
+
+/*
+ * Look for any unwanted ranges between mstart, mend and remove them. This
+ * might lead to split and split ranges are put in ced->mem.ranges[] array
+ */
+static int elf_header_exclude_ranges(struct crash_elf_data *ced,
+ unsigned long long mstart, unsigned long long mend)
+{
+ struct crash_mem *cmem = &ced->mem;
+ int ret = 0;
+
+ memset(cmem->ranges, 0, sizeof(cmem->ranges));
+
+ cmem->ranges[0].start = mstart;
+ cmem->ranges[0].end = mend;
+ cmem->nr_ranges = 1;
+
+ /* Exclude crashkernel region */
+ ret = exclude_mem_range(cmem, crashk_res.start, crashk_res.end);
+ if (ret)
+ return ret;
+
+#ifdef CONFIG_X86_64
+ if (crashk_low_res.end) {
+ ret = exclude_mem_range(cmem, crashk_low_res.start,
+ crashk_low_res.end);
+ if (ret)
+ return ret;
+ }
+#endif
+
+ return ret;
+}
+
+static int prepare_elf64_ram_headers_callback(u64 start, u64 end, void *arg)
+{
+ struct crash_elf_data *ced = arg;
+ Elf64_Ehdr *ehdr;
+ Elf64_Phdr *phdr;
+ unsigned long mstart, mend;
+#ifdef CONFIG_X86_64
+ struct kimage *image = ced->image;
+#endif
+ struct crash_mem *cmem;
+ int ret, i;
+
+ ehdr = ced->ehdr;
+
+ /* Exclude unwanted mem ranges */
+ ret = elf_header_exclude_ranges(ced, start, end);
+ if (ret)
+ return ret;
+
+ /* Go through all the ranges in ced->mem.ranges[] and prepare phdr */
+ cmem = &ced->mem;
+
+ for (i = 0; i < cmem->nr_ranges; i++) {
+ mstart = cmem->ranges[i].start;
+ mend = cmem->ranges[i].end;
+
+ phdr = ced->bufp;
+ ced->bufp += sizeof(Elf64_Phdr);
+
+ phdr->p_type = PT_LOAD;
+ phdr->p_flags = PF_R|PF_W|PF_X;
+ phdr->p_offset = mstart;
+
+#ifdef CONFIG_X86_64
+ /*
+ * If a range matches backup region, adjust offset to backup
+ * segment.
+ */
+ if (mstart == image->arch.backup_src_start &&
+ (mend - mstart + 1) == image->arch.backup_src_sz)
+ phdr->p_offset = image->arch.backup_load_addr;
+#endif
+
+ phdr->p_paddr = mstart;
+ phdr->p_vaddr = (unsigned long long) __va(mstart);
+ phdr->p_filesz = phdr->p_memsz = mend - mstart + 1;
+ phdr->p_align = 0;
+ ehdr->e_phnum++;
+ pr_debug("Crash PT_LOAD elf header. phdr=%p vaddr=0x%llx, paddr=0x%llx, sz=0x%llx e_phnum=%d p_offset=0x%llx\n",
+ phdr, phdr->p_vaddr, phdr->p_paddr, phdr->p_filesz,
+ ehdr->e_phnum, phdr->p_offset);
+ }
+
+ return ret;
+}
+
+static int prepare_elf64_headers(struct crash_elf_data *ced,
+ void **addr, unsigned long *sz)
+{
+ Elf64_Ehdr *ehdr;
+ Elf64_Phdr *phdr;
+ unsigned long nr_cpus = num_possible_cpus(), nr_phdr, elf_sz;
+ unsigned char *buf, *bufp;
+ unsigned int cpu;
+ unsigned long long notes_addr;
+ int ret;
+
+ /* extra phdr for vmcoreinfo elf note */
+ nr_phdr = nr_cpus + 1;
+ nr_phdr += ced->max_nr_ranges;
+
+ /*
+ * kexec-tools creates an extra PT_LOAD phdr for kernel text mapping
+ * area on x86_64 (ffffffff80000000 - ffffffffa0000000).
+ * I think this is required by tools like gdb. So same physical
+ * memory will be mapped in two elf headers. One will contain kernel
+ * text virtual addresses and other will have __va(physical) addresses.
+ */
+
+ nr_phdr++;
+ elf_sz = sizeof(Elf64_Ehdr) + nr_phdr * sizeof(Elf64_Phdr);
+ elf_sz = ALIGN(elf_sz, ELF_CORE_HEADER_ALIGN);
+
+ buf = vzalloc(elf_sz);
+ if (!buf)
+ return -ENOMEM;
+
+ bufp = buf;
+ ehdr = (Elf64_Ehdr *)bufp;
+ bufp += sizeof(Elf64_Ehdr);
+ memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
+ ehdr->e_ident[EI_CLASS] = ELFCLASS64;
+ ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
+ ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+ ehdr->e_ident[EI_OSABI] = ELF_OSABI;
+ memset(ehdr->e_ident + EI_PAD, 0, EI_NIDENT - EI_PAD);
+ ehdr->e_type = ET_CORE;
+ ehdr->e_machine = ELF_ARCH;
+ ehdr->e_version = EV_CURRENT;
+ ehdr->e_phoff = sizeof(Elf64_Ehdr);
+ ehdr->e_ehsize = sizeof(Elf64_Ehdr);
+ ehdr->e_phentsize = sizeof(Elf64_Phdr);
+
+ /* Prepare one phdr of type PT_NOTE for each present cpu */
+ for_each_present_cpu(cpu) {
+ phdr = (Elf64_Phdr *)bufp;
+ bufp += sizeof(Elf64_Phdr);
+ phdr->p_type = PT_NOTE;
+ notes_addr = per_cpu_ptr_to_phys(per_cpu_ptr(crash_notes, cpu));
+ phdr->p_offset = phdr->p_paddr = notes_addr;
+ phdr->p_filesz = phdr->p_memsz = sizeof(note_buf_t);
+ (ehdr->e_phnum)++;
+ }
+
+ /* Prepare one PT_NOTE header for vmcoreinfo */
+ phdr = (Elf64_Phdr *)bufp;
+ bufp += sizeof(Elf64_Phdr);
+ phdr->p_type = PT_NOTE;
+ phdr->p_offset = phdr->p_paddr = paddr_vmcoreinfo_note();
+ phdr->p_filesz = phdr->p_memsz = VMCOREINFO_NOTE_SIZE;
+ (ehdr->e_phnum)++;
+
+#ifdef CONFIG_X86_64
+ /* Prepare PT_LOAD type program header for kernel text region */
+ phdr = (Elf64_Phdr *)bufp;
+ bufp += sizeof(Elf64_Phdr);
+ phdr->p_type = PT_LOAD;
+ phdr->p_flags = PF_R|PF_W|PF_X;
+ phdr->p_vaddr = (Elf64_Addr)_text;
+ phdr->p_filesz = phdr->p_memsz = _end - _text;
+ phdr->p_offset = phdr->p_paddr = __pa_symbol(_text);
+ (ehdr->e_phnum)++;
+#endif
+
+ /* Prepare PT_LOAD headers for system ram chunks. */
+ ced->ehdr = ehdr;
+ ced->bufp = bufp;
+ ret = walk_system_ram_res(0, -1, ced,
+ prepare_elf64_ram_headers_callback);
+ if (ret < 0)
+ return ret;
+
+ *addr = buf;
+ *sz = elf_sz;
+ return 0;
+}
+
+/* Prepare elf headers. Return addr and size */
+int prepare_elf_headers(struct kimage *image, void **addr, unsigned long *sz)
+{
+ struct crash_elf_data *ced;
+ int ret;
+
+ ced = kzalloc(sizeof(*ced), GFP_KERNEL);
+ if (!ced)
+ return -ENOMEM;
+
+ fill_up_crash_elf_data(ced, image);
+
+ /* By default prepare 64bit headers */
+ ret = prepare_elf64_headers(ced, addr, sz);
+ kfree(ced);
+ return ret;
+}
+#endif /* CONFIG_CRASH_CORE */
--
2.14.1
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web