diff options
Diffstat (limited to 'kexec/arch/arm64/kexec-arm64.c')
-rw-r--r-- | kexec/arch/arm64/kexec-arm64.c | 84 |
1 files changed, 56 insertions, 28 deletions
diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c index 6f572ed5..44ca3dba 100644 --- a/kexec/arch/arm64/kexec-arm64.c +++ b/kexec/arch/arm64/kexec-arm64.c @@ -456,22 +456,32 @@ static void fill_property(void *buf, uint64_t val, uint32_t cells) } } -static int fdt_setprop_range(void *fdt, int nodeoffset, - const char *name, struct memory_range *range, +static int fdt_setprop_ranges(void *fdt, int nodeoffset, const char *name, + struct memory_range *ranges, int nr_ranges, bool reverse, uint32_t address_cells, uint32_t size_cells) { void *buf, *prop; size_t buf_size; - int result; + int i, result; + struct memory_range *range; - buf_size = (address_cells + size_cells) * sizeof(uint32_t); + buf_size = (address_cells + size_cells) * sizeof(uint32_t) * nr_ranges; prop = buf = xmalloc(buf_size); + if (!buf) + return -ENOMEM; - fill_property(prop, range->start, address_cells); - prop += address_cells * sizeof(uint32_t); + for (i = 0; i < nr_ranges; i++) { + if (reverse) + range = ranges + (nr_ranges - 1 - i); + else + range = ranges + i; - fill_property(prop, range->end - range->start + 1, size_cells); - prop += size_cells * sizeof(uint32_t); + 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); @@ -493,7 +503,7 @@ static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash) int len, range_len; int nodeoffset; int new_size; - int result, kaslr_seed; + int i, result, kaslr_seed; result = fdt_check_header(dtb->buf); @@ -524,18 +534,20 @@ static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash) 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; + for (i = 0; i < usablemem_rgns.size; i++) { + if (!cells_size_fitted(address_cells, size_cells, + &crash_reserved_mem[i])) { + 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); + + fdt_prop_len(PROP_USABLE_MEM_RANGE, range_len * usablemem_rgns.size); new_buf = xmalloc(new_size); result = fdt_open_into(dtb->buf, new_buf, new_size); @@ -619,8 +631,8 @@ static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash) if (on_crash) { /* add linux,elfcorehdr */ nodeoffset = fdt_path_offset(new_buf, "/chosen"); - result = fdt_setprop_range(new_buf, nodeoffset, - PROP_ELFCOREHDR, &elfcorehdr_mem, + result = fdt_setprop_ranges(new_buf, nodeoffset, + PROP_ELFCOREHDR, &elfcorehdr_mem, 1, false, address_cells, size_cells); if (result) { dbgprintf("%s: fdt_setprop failed: %s\n", __func__, @@ -629,10 +641,17 @@ static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash) goto on_error; } - /* add linux,usable-memory-range */ + /* + * add linux,usable-memory-range + * + * crash dump kernel support one or two regions, to make + * compatibility with existing user-space and older kdump, the + * low region is always the last one. + */ nodeoffset = fdt_path_offset(new_buf, "/chosen"); - result = fdt_setprop_range(new_buf, nodeoffset, - PROP_USABLE_MEM_RANGE, &crash_reserved_mem, + result = fdt_setprop_ranges(new_buf, nodeoffset, + PROP_USABLE_MEM_RANGE, + usablemem_rgns.ranges, usablemem_rgns.size, true, address_cells, size_cells); if (result) { dbgprintf("%s: fdt_setprop failed: %s\n", __func__, @@ -665,13 +684,13 @@ unsigned long arm64_locate_kernel_segment(struct kexec_info *info) if (info->kexec_flags & KEXEC_ON_CRASH) { unsigned long hole_end; - hole = (crash_reserved_mem.start < mem_min ? - mem_min : crash_reserved_mem.start); + hole = (crash_reserved_mem[usablemem_rgns.size - 1].start < mem_min ? + mem_min : crash_reserved_mem[usablemem_rgns.size - 1].start); hole = _ALIGN_UP(hole, MiB(2)); hole_end = hole + arm64_mem.text_offset + arm64_mem.image_size; if ((hole_end > mem_max) || - (hole_end > crash_reserved_mem.end)) { + (hole_end > crash_reserved_mem[usablemem_rgns.size - 1].end)) { dbgprintf("%s: Crash kernel out of range\n", __func__); hole = ULONG_MAX; } @@ -745,7 +764,7 @@ int arm64_load_other_segments(struct kexec_info *info, hole_min = image_base + arm64_mem.image_size; if (info->kexec_flags & KEXEC_ON_CRASH) - hole_max = crash_reserved_mem.end; + hole_max = crash_reserved_mem[usablemem_rgns.size - 1].end; else hole_max = ULONG_MAX; @@ -979,12 +998,21 @@ int get_phys_base_from_pt_load(unsigned long *phys_offset) return 0; } -static bool to_be_excluded(char *str) +static bool to_be_excluded(char *str, unsigned long long start, unsigned long long end) { + if (!strncmp(str, CRASH_KERNEL, strlen(CRASH_KERNEL))) { + uint64_t load_start, load_end; + + if (!get_crash_kernel_load_range(&load_start, &load_end) && + (load_start == start) && (load_end == end)) + return false; + + return true; + } + if (!strncmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM)) || !strncmp(str, KERNEL_CODE, strlen(KERNEL_CODE)) || - !strncmp(str, KERNEL_DATA, strlen(KERNEL_DATA)) || - !strncmp(str, CRASH_KERNEL, strlen(CRASH_KERNEL))) + !strncmp(str, KERNEL_DATA, strlen(KERNEL_DATA))) return false; else return true; @@ -1066,7 +1094,7 @@ int get_memory_ranges(struct memory_range **range, int *ranges, memranges.size - 1, memranges.ranges[memranges.size - 1].start, memranges.ranges[memranges.size - 1].end); - } else if (to_be_excluded(str)) { + } else if (to_be_excluded(str, start, end)) { if (!memranges.size) continue; |