Blame SOURCES/kexec-tools-2.0.20-arm64-kexec-allocate-memory-space-avoiding-reserved-.patch

2c29b9
From f736104f533290b4ce6fbfbca74abde9ffd3888c Mon Sep 17 00:00:00 2001
2c29b9
From: AKASHI Takahiro <takahiro.akashi@linaro.org>
2c29b9
Date: Wed, 18 Dec 2019 11:42:31 -0500
2c29b9
Subject: [PATCH 2/3] arm64: kexec: allocate memory space avoiding reserved
2c29b9
 regions
2c29b9
2c29b9
On UEFI/ACPI-only system, some memory regions, including but not limited
2c29b9
to UEFI memory map and ACPI tables, must be preserved across kexec'ing.
2c29b9
Otherwise, they can be corrupted and result in early failure in booting
2c29b9
a new kernel.
2c29b9
2c29b9
In recent kernels, /proc/iomem now has an extended file format like:
2c29b9
2c29b9
 40000000-5871ffff : System RAM
2c29b9
   41800000-426affff : Kernel code
2c29b9
   426b0000-42aaffff : reserved
2c29b9
   42ab0000-42c64fff : Kernel data
2c29b9
   54400000-583fffff : Crash kernel
2c29b9
   58590000-585effff : reserved
2c29b9
   58700000-5871ffff : reserved
2c29b9
 58720000-58b5ffff : reserved
2c29b9
 58b60000-5be3ffff : System RAM
2c29b9
   58b61000-58b61fff : reserved
2c29b9
2c29b9
where the "reserved" entries at the top level or under System RAM (and
2c29b9
its descendant resources) are ones of such kind and should not be regarded
2c29b9
as usable memory ranges where several free spaces for loading kexec data
2c29b9
will be allocated.
2c29b9
2c29b9
With this patch, get_memory_ranges() will handle this format of file
2c29b9
correctly. Note that, for safety, unknown regions, in addition to
2c29b9
"reserved" ones, will also be excluded.
2c29b9
2c29b9
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
2c29b9
Tested-by: Bhupesh Sharma <bhsharma@redhat.com>
2c29b9
Tested-by: Masayoshi Mizuma <m.mizuma@jp.fujitsu.com>
2c29b9
Signed-off-by: Simon Horman <horms@verge.net.au>
2c29b9
---
2c29b9
 kexec/arch/arm64/kexec-arm64.c | 153 +++++++++++++++++++++++++----------------
2c29b9
 1 file changed, 94 insertions(+), 59 deletions(-)
2c29b9
2c29b9
diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c
2c29b9
index 6ad3b0a134b3..45ebc54a9b6f 100644
2c29b9
--- a/kexec/arch/arm64/kexec-arm64.c
2c29b9
+++ b/kexec/arch/arm64/kexec-arm64.c
2c29b9
@@ -10,7 +10,9 @@
2c29b9
 #include <inttypes.h>
2c29b9
 #include <libfdt.h>
2c29b9
 #include <limits.h>
2c29b9
+#include <stdio.h>
2c29b9
 #include <stdlib.h>
2c29b9
+#include <string.h>
2c29b9
 #include <sys/stat.h>
2c29b9
 #include <linux/elf-em.h>
2c29b9
 #include <elf.h>
2c29b9
@@ -29,6 +31,7 @@
2c29b9
 #include "fs2dt.h"
2c29b9
 #include "iomem.h"
2c29b9
 #include "kexec-syscall.h"
2c29b9
+#include "mem_regions.h"
2c29b9
 #include "arch/options.h"
2c29b9
 
2c29b9
 #define ROOT_NODE_ADDR_CELLS_DEFAULT 1
2c29b9
@@ -905,19 +908,33 @@ int get_phys_base_from_pt_load(unsigned long *phys_offset)
2c29b9
 	return 0;
2c29b9
 }
2c29b9
 
2c29b9
+static bool to_be_excluded(char *str)
2c29b9
+{
2c29b9
+	if (!strncmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM)) ||
2c29b9
+	    !strncmp(str, KERNEL_CODE, strlen(KERNEL_CODE)) ||
2c29b9
+	    !strncmp(str, KERNEL_DATA, strlen(KERNEL_DATA)) ||
2c29b9
+	    !strncmp(str, CRASH_KERNEL, strlen(CRASH_KERNEL)))
2c29b9
+		return false;
2c29b9
+	else
2c29b9
+		return true;
2c29b9
+}
2c29b9
+
2c29b9
 /**
2c29b9
- * get_memory_ranges_iomem_cb - Helper for get_memory_ranges_iomem.
2c29b9
+ * get_memory_ranges - Try to get the memory ranges from
2c29b9
+ * /proc/iomem.
2c29b9
  */
2c29b9
-
2c29b9
-static int get_memory_ranges_iomem_cb(void *data, int nr, char *str,
2c29b9
-	unsigned long long base, unsigned long long length)
2c29b9
+int get_memory_ranges(struct memory_range **range, int *ranges,
2c29b9
+	unsigned long kexec_flags)
2c29b9
 {
2c29b9
-	int ret;
2c29b9
 	unsigned long phys_offset = UINT64_MAX;
2c29b9
-	struct memory_range *r;
2c29b9
-
2c29b9
-	if (nr >= KEXEC_SEGMENT_MAX)
2c29b9
-		return -1;
2c29b9
+	FILE *fp;
2c29b9
+	const char *iomem = proc_iomem();
2c29b9
+	char line[MAX_LINE], *str;
2c29b9
+	unsigned long long start, end;
2c29b9
+	int n, consumed;
2c29b9
+	struct memory_ranges memranges;
2c29b9
+	struct memory_range *last, excl_range;
2c29b9
+	int ret;
2c29b9
 
2c29b9
 	if (!try_read_phys_offset_from_kcore) {
2c29b9
 		/* Since kernel version 4.19, 'kcore' contains
2c29b9
@@ -951,17 +968,72 @@ static int get_memory_ranges_iomem_cb(void *data, int nr, char *str,
2c29b9
 		try_read_phys_offset_from_kcore = true;
2c29b9
 	}
2c29b9
 
2c29b9
-	r = (struct memory_range *)data + nr;
2c29b9
+	fp = fopen(iomem, "r");
2c29b9
+	if (!fp)
2c29b9
+		die("Cannot open %s\n", iomem);
2c29b9
+
2c29b9
+	memranges.ranges = NULL;
2c29b9
+	memranges.size = memranges.max_size  = 0;
2c29b9
+
2c29b9
+	while (fgets(line, sizeof(line), fp) != 0) {
2c29b9
+		n = sscanf(line, "%llx-%llx : %n", &start, &end, &consumed);
2c29b9
+		if (n != 2)
2c29b9
+			continue;
2c29b9
+		str = line + consumed;
2c29b9
+
2c29b9
+		if (!strncmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM))) {
2c29b9
+			ret = mem_regions_alloc_and_add(&memranges,
2c29b9
+					start, end - start + 1, RANGE_RAM);
2c29b9
+			if (ret) {
2c29b9
+				fprintf(stderr,
2c29b9
+					"Cannot allocate memory for ranges\n");
2c29b9
+				fclose(fp);
2c29b9
+				return -ENOMEM;
2c29b9
+			}
2c29b9
 
2c29b9
-	if (!strncmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM)))
2c29b9
-		r->type = RANGE_RAM;
2c29b9
-	else if (!strncmp(str, IOMEM_RESERVED, strlen(IOMEM_RESERVED)))
2c29b9
-		r->type = RANGE_RESERVED;
2c29b9
-	else
2c29b9
-		return 1;
2c29b9
+			dbgprintf("%s:+[%d] %016llx - %016llx\n", __func__,
2c29b9
+				memranges.size - 1,
2c29b9
+				memranges.ranges[memranges.size - 1].start,
2c29b9
+				memranges.ranges[memranges.size - 1].end);
2c29b9
+		} else if (to_be_excluded(str)) {
2c29b9
+			if (!memranges.size)
2c29b9
+				continue;
2c29b9
+
2c29b9
+			/*
2c29b9
+			 * Note: mem_regions_exclude() doesn't guarantee
2c29b9
+			 * that the ranges are sorted out, but as long as
2c29b9
+			 * we cope with /proc/iomem, we only operate on
2c29b9
+			 * the last entry and so it is safe.
2c29b9
+			 */
2c29b9
 
2c29b9
-	r->start = base;
2c29b9
-	r->end = base + length - 1;
2c29b9
+			/* The last System RAM range */
2c29b9
+			last = &memranges.ranges[memranges.size - 1];
2c29b9
+
2c29b9
+			if (last->end < start)
2c29b9
+				/* New resource outside of System RAM */
2c29b9
+				continue;
2c29b9
+			if (end < last->start)
2c29b9
+				/* Already excluded by parent resource */
2c29b9
+				continue;
2c29b9
+
2c29b9
+			excl_range.start = start;
2c29b9
+			excl_range.end = end;
2c29b9
+			ret = mem_regions_alloc_and_exclude(&memranges, &excl_range);
2c29b9
+			if (ret) {
2c29b9
+				fprintf(stderr,
2c29b9
+					"Cannot allocate memory for ranges (exclude)\n");
2c29b9
+				fclose(fp);
2c29b9
+				return -ENOMEM;
2c29b9
+			}
2c29b9
+			dbgprintf("%s:-      %016llx - %016llx\n",
2c29b9
+					__func__, start, end);
2c29b9
+		}
2c29b9
+	}
2c29b9
+
2c29b9
+	fclose(fp);
2c29b9
+
2c29b9
+	*range = memranges.ranges;
2c29b9
+	*ranges = memranges.size;
2c29b9
 
2c29b9
 	/* As a fallback option, we can try determining the PHYS_OFFSET
2c29b9
 	 * value from the '/proc/iomem' entries as well.
2c29b9
@@ -982,52 +1054,15 @@ static int get_memory_ranges_iomem_cb(void *data, int nr, char *str,
2c29b9
 	 * between the user-space and kernel space 'PHYS_OFFSET'
2c29b9
 	 * value.
2c29b9
 	 */
2c29b9
-	set_phys_offset(r->start, "iomem");
2c29b9
-
2c29b9
-	dbgprintf("%s: %016llx - %016llx : %s", __func__, r->start,
2c29b9
-		r->end, str);
2c29b9
-
2c29b9
-	return 0;
2c29b9
-}
2c29b9
-
2c29b9
-/**
2c29b9
- * get_memory_ranges_iomem - Try to get the memory ranges from
2c29b9
- * /proc/iomem.
2c29b9
- */
2c29b9
+	if (memranges.size)
2c29b9
+		set_phys_offset(memranges.ranges[0].start, "iomem");
2c29b9
 
2c29b9
-static int get_memory_ranges_iomem(struct memory_range *array,
2c29b9
-	unsigned int *count)
2c29b9
-{
2c29b9
-	*count = kexec_iomem_for_each_line(NULL,
2c29b9
-		get_memory_ranges_iomem_cb, array);
2c29b9
-
2c29b9
-	if (!*count) {
2c29b9
-		dbgprintf("%s: failed: No RAM found.\n", __func__);
2c29b9
-		return EFAILED;
2c29b9
-	}
2c29b9
+	dbgprint_mem_range("System RAM ranges;",
2c29b9
+				memranges.ranges, memranges.size);
2c29b9
 
2c29b9
 	return 0;
2c29b9
 }
2c29b9
 
2c29b9
-/**
2c29b9
- * get_memory_ranges - Try to get the memory ranges some how.
2c29b9
- */
2c29b9
-
2c29b9
-int get_memory_ranges(struct memory_range **range, int *ranges,
2c29b9
-	unsigned long kexec_flags)
2c29b9
-{
2c29b9
-	static struct memory_range array[KEXEC_SEGMENT_MAX];
2c29b9
-	unsigned int count;
2c29b9
-	int result;
2c29b9
-
2c29b9
-	result = get_memory_ranges_iomem(array, &count);
2c29b9
-
2c29b9
-	*range = result ? NULL : array;
2c29b9
-	*ranges = result ? 0 : count;
2c29b9
-
2c29b9
-	return result;
2c29b9
-}
2c29b9
-
2c29b9
 int arch_compat_trampoline(struct kexec_info *info)
2c29b9
 {
2c29b9
 	return 0;
2c29b9
-- 
2c29b9
2.7.4
2c29b9