History

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.

AKASHI Takahiro committed 6 years ago

Commit f7361041 parent cf977b11 file · +94 −59Browse files

Unified · Split

kexec/arch/arm64/kexec-arm64.c+94−59
@@ -10,7 +10,9 @@ · 9 unmodified lines
1010 #include <inttypes.h>
1111 #include <libfdt.h>
1212 #include <limits.h>
13+#include <stdio.h>
1314 #include <stdlib.h>
15+#include <string.h>
1416 #include <sys/stat.h>
1517 #include <linux/elf-em.h>
1618 #include <elf.h>
@@ -29,6 +31,7 @@ · 12 unmodified lines
2931 #include "fs2dt.h"
3032 #include "iomem.h"
3133 #include "kexec-syscall.h"
34+#include "mem_regions.h"
3235 #include "arch/options.h"
3336
3437 #define ROOT_NODE_ADDR_CELLS_DEFAULT 1
@@ -905,19 +908,33 @@ int get_phys_base_from_pt_load(unsigned long *phys_offset) · 870 unmodified lines
905908 return 0;
906909 }
907910
911+static bool to_be_excluded(char *str)
912+{
913+ if (!strncmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM)) ||
914+ !strncmp(str, KERNEL_CODE, strlen(KERNEL_CODE)) ||
915+ !strncmp(str, KERNEL_DATA, strlen(KERNEL_DATA)) ||
916+ !strncmp(str, CRASH_KERNEL, strlen(CRASH_KERNEL)))
917+ return false;
918+ else
919+ return true;
920+}
921+
908922 /**
909− * get_memory_ranges_iomem_cb - Helper for get_memory_ranges_iomem.
923+ * get_memory_ranges - Try to get the memory ranges from
924+ * /proc/iomem.
910925 */
911−
912−static int get_memory_ranges_iomem_cb(void *data, int nr, char *str,
913− unsigned long long base, unsigned long long length)
926+int get_memory_ranges(struct memory_range **range, int *ranges,
927+ unsigned long kexec_flags)
914928 {
915− int ret;
916929 unsigned long phys_offset = UINT64_MAX;
917− struct memory_range *r;
918−
919− if (nr >= KEXEC_SEGMENT_MAX)
920− return -1;
930+ FILE *fp;
931+ const char *iomem = proc_iomem();
932+ char line[MAX_LINE], *str;
933+ unsigned long long start, end;
934+ int n, consumed;
935+ struct memory_ranges memranges;
936+ struct memory_range *last, excl_range;
937+ int ret;
921938
922939 if (!try_read_phys_offset_from_kcore) {
923940 /* Since kernel version 4.19, 'kcore' contains
@@ -951,17 +968,72 @@ static int get_memory_ranges_iomem_cb(void *data, int nr, char *str, · 27 unmodified lines
951968 try_read_phys_offset_from_kcore = true;
952969 }
953970
954− r = (struct memory_range *)data + nr;
971+ fp = fopen(iomem, "r");
972+ if (!fp)
973+ die("Cannot open %s\n", iomem);
974+
975+ memranges.ranges = NULL;
976+ memranges.size = memranges.max_size = 0;
977+
978+ while (fgets(line, sizeof(line), fp) != 0) {
979+ n = sscanf(line, "%llx-%llx : %n", &start, &end, &consumed);
980+ if (n != 2)
981+ continue;
982+ str = line + consumed;
983+
984+ if (!strncmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM))) {
985+ ret = mem_regions_alloc_and_add(&memranges,
986+ start, end - start + 1, RANGE_RAM);
987+ if (ret) {
988+ fprintf(stderr,
989+ "Cannot allocate memory for ranges\n");
990+ fclose(fp);
991+ return -ENOMEM;
992+ }
955993
956− if (!strncmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM)))
957− r->type = RANGE_RAM;
958− else if (!strncmp(str, IOMEM_RESERVED, strlen(IOMEM_RESERVED)))
959− r->type = RANGE_RESERVED;
960− else
961− return 1;
994+ dbgprintf("%s:+[%d] %016llx - %016llx\n", __func__,
995+ memranges.size - 1,
996+ memranges.ranges[memranges.size - 1].start,
997+ memranges.ranges[memranges.size - 1].end);
998+ } else if (to_be_excluded(str)) {
999+ if (!memranges.size)
1000+ continue;
1001+
1002+ /*
1003+ * Note: mem_regions_exclude() doesn't guarantee
1004+ * that the ranges are sorted out, but as long as
1005+ * we cope with /proc/iomem, we only operate on
1006+ * the last entry and so it is safe.
1007+ */
9621008
963− r->start = base;
964− r->end = base + length - 1;
1009+ /* The last System RAM range */
1010+ last = &memranges.ranges[memranges.size - 1];
1011+
1012+ if (last->end < start)
1013+ /* New resource outside of System RAM */
1014+ continue;
1015+ if (end < last->start)
1016+ /* Already excluded by parent resource */
1017+ continue;
1018+
1019+ excl_range.start = start;
1020+ excl_range.end = end;
1021+ ret = mem_regions_alloc_and_exclude(&memranges, &excl_range);
1022+ if (ret) {
1023+ fprintf(stderr,
1024+ "Cannot allocate memory for ranges (exclude)\n");
1025+ fclose(fp);
1026+ return -ENOMEM;
1027+ }
1028+ dbgprintf("%s:- %016llx - %016llx\n",
1029+ __func__, start, end);
1030+ }
1031+ }
1032+
1033+ fclose(fp);
1034+
1035+ *range = memranges.ranges;
1036+ *ranges = memranges.size;
9651037
9661038 /* As a fallback option, we can try determining the PHYS_OFFSET
9671039 * 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, · 14 unmodified lines
9821054 * between the user-space and kernel space 'PHYS_OFFSET'
9831055 * value.
9841056 */
985− set_phys_offset(r->start, "iomem");
986−
987− dbgprintf("%s: %016llx - %016llx : %s", __func__, r->start,
988− r->end, str);
989−
990− return 0;
991−}
992−
993−/**
994− * get_memory_ranges_iomem - Try to get the memory ranges from
995− * /proc/iomem.
996− */
1057+ if (memranges.size)
1058+ set_phys_offset(memranges.ranges[0].start, "iomem");
9971059
998−static int get_memory_ranges_iomem(struct memory_range *array,
999− unsigned int *count)
1000−{
1001− *count = kexec_iomem_for_each_line(NULL,
1002− get_memory_ranges_iomem_cb, array);
1003−
1004− if (!*count) {
1005− dbgprintf("%s: failed: No RAM found.\n", __func__);
1006− return EFAILED;
1007− }
1060+ dbgprint_mem_range("System RAM ranges;",
1061+ memranges.ranges, memranges.size);
10081062
10091063 return 0;
10101064 }
10111065
1012−/**
1013− * get_memory_ranges - Try to get the memory ranges some how.
1014− */
1015−
1016−int get_memory_ranges(struct memory_range **range, int *ranges,
1017− unsigned long kexec_flags)
1018−{
1019− static struct memory_range array[KEXEC_SEGMENT_MAX];
1020− unsigned int count;
1021− int result;
1022−
1023− result = get_memory_ranges_iomem(array, &count);
1024−
1025− *range = result ? NULL : array;
1026− *ranges = result ? 0 : count;
1027−
1028− return result;
1029−}
1030−
10311066 int arch_compat_trampoline(struct kexec_info *info)
10321067 {
10331068 return 0;

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t