summaryrefslogtreecommitdiffstats
path: root/kexec/arch/arm64/kexec-arm64.c
diff options
context:
space:
mode:
Diffstat (limited to 'kexec/arch/arm64/kexec-arm64.c')
-rw-r--r--kexec/arch/arm64/kexec-arm64.c84
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;