From 7c59b94be4abf8b28dd02bae8b79542377a86618 Mon Sep 17 00:00:00 2001 Message-Id: <7c59b94be4abf8b28dd02bae8b79542377a86618.1489676829.git.panand@redhat.com> In-Reply-To: References: From: AKASHI Takahiro Date: Wed, 15 Mar 2017 18:38:23 +0900 Subject: [PATCH 09/10] arm64: kdump: add DT properties to crash dump kernel's dtb We pass the following properties to crash dump kernel: linux,elfcorehdr: elf core header segment, same as "elfcorehdr=" kernel parameter on other archs linux,usable-memory-range: usable memory reserved for crash dump kernel Signed-off-by: AKASHI Takahiro --- kexec/arch/arm64/kexec-arm64.c | 197 ++++++++++++++++++++++++++++++++++++- kexec/arch/arm64/kexec-elf-arm64.c | 5 - 2 files changed, 192 insertions(+), 10 deletions(-) diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c index 5e30107b043f..f3f101d1be48 100644 --- a/kexec/arch/arm64/kexec-arm64.c +++ b/kexec/arch/arm64/kexec-arm64.c @@ -25,6 +25,14 @@ #include "kexec-syscall.h" #include "arch/options.h" +#define ROOT_NODE_ADDR_CELLS_DEFAULT 1 +#define ROOT_NODE_SIZE_CELLS_DEFAULT 1 + +#define PROP_ADDR_CELLS "#address-cells" +#define PROP_SIZE_CELLS "#size-cells" +#define PROP_ELFCOREHDR "linux,elfcorehdr" +#define PROP_USABLE_MEM_RANGE "linux,usable-memory-range" + /* Global varables the core kexec routines expect. */ unsigned char reuse_initrd; @@ -128,9 +136,6 @@ int arch_process_options(int argc, char **argv) case OPT_INITRD: arm64_opts.initrd = optarg; break; - case OPT_PANIC: - die("load-panic (-p) not supported"); - break; default: break; /* Ignore core and unknown options. */ } @@ -281,12 +286,115 @@ on_success: return 0; } +static int get_cells_size(void *fdt, uint32_t *address_cells, + uint32_t *size_cells) +{ + int nodeoffset; + const uint32_t *prop = NULL; + int prop_len; + + /* default values */ + *address_cells = ROOT_NODE_ADDR_CELLS_DEFAULT; + *size_cells = ROOT_NODE_SIZE_CELLS_DEFAULT; + + /* under root node */ + nodeoffset = fdt_path_offset(fdt, "/"); + if (nodeoffset < 0) + goto on_error; + + prop = fdt_getprop(fdt, nodeoffset, PROP_ADDR_CELLS, &prop_len); + if (prop) { + if (prop_len == sizeof(*prop)) + *address_cells = fdt32_to_cpu(*prop); + else + goto on_error; + } + + prop = fdt_getprop(fdt, nodeoffset, PROP_SIZE_CELLS, &prop_len); + if (prop) { + if (prop_len == sizeof(*prop)) + *size_cells = fdt32_to_cpu(*prop); + else + goto on_error; + } + + dbgprintf("%s: #address-cells:%d #size-cells:%d\n", __func__, + *address_cells, *size_cells); + return 0; + +on_error: + return -EFAILED; +} + +bool cells_size_fitted(uint32_t address_cells, uint32_t size_cells, + struct memory_range *range) +{ + dbgprintf("%s: %llx-%llx\n", __func__, range->start, range->end); + + /* if *_cells >= 2, cells can hold 64-bit values anyway */ + if ((address_cells == 1) && (range->start >= (1ULL << 32))) + return false; + + if ((size_cells == 1) && + ((range->end - range->start + 1) >= (1ULL << 32))) + return false; + + return true; +} + +static void fill_property(void *buf, uint64_t val, uint32_t cells) +{ + uint32_t val32; + int i; + + if (cells == 1) { + val32 = cpu_to_fdt32((uint32_t)val); + memcpy(buf, &val32, sizeof(uint32_t)); + } else { + for (i = 0; + i < (cells * sizeof(uint32_t) - sizeof(uint64_t)); i++) + *(char *)buf++ = 0; + + val = cpu_to_fdt64(val); + memcpy(buf, &val, sizeof(uint64_t)); + } +} + +static int setprop_range(void *fdt, int nodeoffset, + const char *name, struct memory_range *range, + uint32_t address_cells, uint32_t size_cells) +{ + void *buf, *prop; + size_t buf_size; + int result; + + buf_size = (address_cells + size_cells) * sizeof(uint32_t); + prop = buf = xmalloc(buf_size); + + fill_property(prop, range->start, address_cells); + prop += address_cells * sizeof(uint32_t); + + fill_property(prop, range->end - range->start + 1, size_cells); + prop += size_cells * sizeof(uint32_t); + + result = fdt_setprop(fdt, nodeoffset, name, buf, buf_size); + + free(buf); + + return result; +} + /** * setup_2nd_dtb - Setup the 2nd stage kernel's dtb. */ -static int setup_2nd_dtb(struct dtb *dtb, char *command_line) +static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash) { + uint32_t address_cells, size_cells; + int range_len; + int nodeoffset; + char *new_buf = NULL; + int new_size; int result; result = fdt_check_header(dtb->buf); @@ -298,8 +406,86 @@ static int setup_2nd_dtb(struct dtb *dtb, char *command_line) result = set_bootargs(dtb, command_line); + if (on_crash) { + /* determine #address-cells and #size-cells */ + result = get_cells_size(dtb->buf, &address_cells, &size_cells); + if (result) { + fprintf(stderr, + "kexec: cannot determine cells-size.\n"); + result = -EINVAL; + goto on_error; + } + + if (!cells_size_fitted(address_cells, size_cells, + &elfcorehdr_mem)) { + fprintf(stderr, + "kexec: elfcorehdr doesn't fit cells-size.\n"); + result = -EINVAL; + goto on_error; + } + + if (!cells_size_fitted(address_cells, size_cells, + &crash_reserved_mem)) { + fprintf(stderr, + "kexec: usable memory range doesn't fit cells-size.\n"); + result = -EINVAL; + goto on_error; + } + + /* duplicate dt blob */ + range_len = sizeof(uint32_t) * (address_cells + size_cells); + new_size = fdt_totalsize(dtb->buf) + + fdt_prop_len(PROP_ELFCOREHDR, range_len) + + fdt_prop_len(PROP_USABLE_MEM_RANGE, range_len); + + new_buf = xmalloc(new_size); + result = fdt_open_into(dtb->buf, new_buf, new_size); + if (result) { + dbgprintf("%s: fdt_open_into failed: %s\n", __func__, + fdt_strerror(result)); + result = -ENOSPC; + goto on_error; + } + + /* add linux,elfcorehdr */ + nodeoffset = fdt_path_offset(new_buf, "/chosen"); + result = setprop_range(new_buf, nodeoffset, + PROP_ELFCOREHDR, &elfcorehdr_mem, + address_cells, size_cells); + if (result) { + dbgprintf("%s: fdt_setprop failed: %s\n", __func__, + fdt_strerror(result)); + result = -EINVAL; + goto on_error; + } + + /* add linux,usable-memory-range */ + nodeoffset = fdt_path_offset(new_buf, "/chosen"); + result = setprop_range(new_buf, nodeoffset, + PROP_USABLE_MEM_RANGE, &crash_reserved_mem, + address_cells, size_cells); + if (result) { + dbgprintf("%s: fdt_setprop failed: %s\n", __func__, + fdt_strerror(result)); + result = -EINVAL; + goto on_error; + } + + fdt_pack(new_buf); + dtb->buf = new_buf; + dtb->size = fdt_totalsize(new_buf); + } + dump_reservemap(dtb); + + return result; + +on_error: + fprintf(stderr, "kexec: %s failed.\n", __func__); + if (new_buf) + free(new_buf); + return result; } @@ -367,7 +553,8 @@ int arm64_load_other_segments(struct kexec_info *info, } } - result = setup_2nd_dtb(&dtb, command_line); + result = setup_2nd_dtb(&dtb, command_line, + info->kexec_flags & KEXEC_ON_CRASH); if (result) return -EFAILED; diff --git a/kexec/arch/arm64/kexec-elf-arm64.c b/kexec/arch/arm64/kexec-elf-arm64.c index 842ce21e2387..b17a31afa24e 100644 --- a/kexec/arch/arm64/kexec-elf-arm64.c +++ b/kexec/arch/arm64/kexec-elf-arm64.c @@ -47,11 +47,6 @@ int elf_arm64_load(int argc, char **argv, const char *kernel_buf, int result; int i; - if (info->kexec_flags & KEXEC_ON_CRASH) { - fprintf(stderr, "kexec: kdump not yet supported on arm64\n"); - return -EFAILED; - } - result = build_elf_exec_info(kernel_buf, kernel_size, &ehdr, 0); if (result < 0) { -- 2.9.3