summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCliff Wickman <cpw@sgi.com>2010-07-26 16:35:34 -0500
committerSimon Horman <horms@verge.net.au>2010-07-29 17:22:08 +0900
commit647f1b8496a2c6da9da2f1df7aa904bb575641e4 (patch)
treeb488757e4de9f6b687e375692898dd2e116db8c8
parent0775c60eb01831209d80f26f71ac0afde0d6593a (diff)
kexec: fix /sys/firmware/memmap memory range overlaps
The memory ranges derived from /sys/firmware/memmap may overlap, causing the kexec command to fail to load the crash kernel. The typical failure reports 'Overlapping memory segments at 0x...'. The preferred remedy might be to fix the BIOS or the kernel, but given that they may at times generate overlaps, a check in the kexec command will prevent crash dumps from being effectively disabled. Diffed against git.kernel.org/pub/scm/linux/kernel/git/horms/kexec-tools.git Signed-off-by: Cliff Wickman <cpw@sgi.com> Signed-off-by: Simon Horman <horms@verge.net.au>
-rw-r--r--kexec/arch/i386/kexec-x86-common.c78
1 files changed, 76 insertions, 2 deletions
diff --git a/kexec/arch/i386/kexec-x86-common.c b/kexec/arch/i386/kexec-x86-common.c
index a468272..ba54973 100644
--- a/kexec/arch/i386/kexec-x86-common.c
+++ b/kexec/arch/i386/kexec-x86-common.c
@@ -129,6 +129,78 @@ static int get_memory_ranges_sysfs(struct memory_range **range, int *ranges)
return 0;
}
+static void remove_range(struct memory_range *range, int nr_ranges, int index)
+{
+ int i, j;
+
+ for (i = index; i < (nr_ranges-1); i++) {
+ j = i+1;
+ range[i] = range[j];
+ }
+}
+
+/**
+ * Verifies and corrects any overlapping ranges.
+ * The ranges array is assumed to be sorted already.
+ *
+ * @param[out] range pointer that will be set to an array that holds the
+ * memory ranges
+ * @param[out] ranges number of ranges valid in @p range
+ *
+ * @return 0 on success, any other value on failure.
+ */
+static int fixup_memory_ranges_sysfs(struct memory_range **range, int *ranges)
+{
+ int i;
+ int j;
+ int change_made;
+ int nr_ranges = *ranges;
+ struct memory_range *rp = *range;
+
+again:
+ change_made = 0;
+ for (i = 0; i < (nr_ranges-1); i++) {
+ j = i+1;
+ if (rp[i].start > rp[j].start) {
+ fprintf(stderr, "sysfs memory out of order!!\n");
+ return 1;
+ }
+
+ if (rp[i].type != rp[j].type)
+ continue;
+
+ if (rp[i].start == rp[j].start) {
+ if (rp[i].end >= rp[j].end) {
+ remove_range(rp, nr_ranges, j);
+ nr_ranges--;
+ change_made++;
+ } else {
+ remove_range(rp, nr_ranges, i);
+ nr_ranges--;
+ change_made++;
+ }
+ } else {
+ if (rp[i].end > rp[j].start) {
+ if (rp[i].end < rp[j].end) {
+ rp[j].start = rp[i].end;
+ change_made++;
+ } else if (rp[i].end >= rp[j].end) {
+ remove_range(rp, nr_ranges, j);
+ nr_ranges--;
+ change_made++;
+ }
+ }
+ }
+ }
+
+ /* fixing/removing an entry may make it wrong relative to the next */
+ if (change_made)
+ goto again;
+
+ *ranges = nr_ranges;
+ return 0;
+}
+
/**
* Return a sorted list of memory ranges.
*
@@ -155,9 +227,11 @@ int get_memory_ranges(struct memory_range **range, int *ranges,
* even if we have /sys/firmware/memmap. Without that, /proc/vmcore
* is empty in the kdump kernel.
*/
- if (!xen_present() && have_sys_firmware_memmap())
+ if (!xen_present() && have_sys_firmware_memmap()) {
ret = get_memory_ranges_sysfs(range, ranges);
- else
+ if (!ret)
+ ret = fixup_memory_ranges_sysfs(range, ranges);
+ } else
ret = get_memory_ranges_proc_iomem(range, ranges);
/*