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