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
10#include <inttypes.h>10#include <inttypes.h>
11#include <libfdt.h>11#include <libfdt.h>
12#include <limits.h>12#include <limits.h>
13#include <stdio.h>
13#include <stdlib.h>14#include <stdlib.h>
15#include <string.h>
14#include <sys/stat.h>16#include <sys/stat.h>
15#include <linux/elf-em.h>17#include <linux/elf-em.h>
16#include <elf.h>18#include <elf.h>
@@ -29,6 +31,7 @@ · 12 unmodified lines
29#include "fs2dt.h"31#include "fs2dt.h"
30#include "iomem.h"32#include "iomem.h"
31#include "kexec-syscall.h"33#include "kexec-syscall.h"
34#include "mem_regions.h"
32#include "arch/options.h"35#include "arch/options.h"
33 36
34#define ROOT_NODE_ADDR_CELLS_DEFAULT 137#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
905 return 0;908 return 0;
906}909}
907 910
911static 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
908/**922/**
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.
910 */925 */
911 926int get_memory_ranges(struct memory_range **range, int *ranges,
912static int get_memory_ranges_iomem_cb(void *data, int nr, char *str,927 unsigned long kexec_flags)
913 unsigned long long base, unsigned long long length)
914{928{
915 int ret;
916 unsigned long phys_offset = UINT64_MAX;929 unsigned long phys_offset = UINT64_MAX;
917 struct memory_range *r;930 FILE *fp;
918 931 const char *iomem = proc_iomem();
919 if (nr >= KEXEC_SEGMENT_MAX)932 char line[MAX_LINE], *str;
920 return -1;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;
921 938
922 if (!try_read_phys_offset_from_kcore) {939 if (!try_read_phys_offset_from_kcore) {
923 /* Since kernel version 4.19, 'kcore' contains940 /* 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
951 try_read_phys_offset_from_kcore = true;968 try_read_phys_offset_from_kcore = true;
952 }969 }
953 970
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 }
955 993
956 if (!strncmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM)))994 dbgprintf("%s:+[%d] %016llx - %016llx\n", __func__,
957 r->type = RANGE_RAM;995 memranges.size - 1,
958 else if (!strncmp(str, IOMEM_RESERVED, strlen(IOMEM_RESERVED)))996 memranges.ranges[memranges.size - 1].start,
959 r->type = RANGE_RESERVED;997 memranges.ranges[memranges.size - 1].end);
960 else998 } else if (to_be_excluded(str)) {
961 return 1;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 */
962 1008
963 r->start = base;1009 /* The last System RAM range */
964 r->end = base + length - 1;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;
965 1037
966 /* As a fallback option, we can try determining the PHYS_OFFSET1038 /* As a fallback option, we can try determining the PHYS_OFFSET
967 * value from the '/proc/iomem' entries as well.1039 * 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
982 * between the user-space and kernel space 'PHYS_OFFSET'1054 * between the user-space and kernel space 'PHYS_OFFSET'
983 * value.1055 * value.
984 */1056 */
985 set_phys_offset(r->start, "iomem");1057 if (memranges.size)
986 1058 set_phys_offset(memranges.ranges[0].start, "iomem");
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 */
997 1059
998static int get_memory_ranges_iomem(struct memory_range *array,1060 dbgprint_mem_range("System RAM ranges;",
999 unsigned int *count)1061 memranges.ranges, memranges.size);
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 }
1008 1062
1009 return 0;1063 return 0;
1010}1064}
1011 1065
1012/**
1013 * get_memory_ranges - Try to get the memory ranges some how.
1014 */
1015
1016int 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
1031int arch_compat_trampoline(struct kexec_info *info)1066int arch_compat_trampoline(struct kexec_info *info)
1032{1067{
1033 return 0;1068 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