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.
Unified · Split
| @@ -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 1 | 37 | #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 | ||
| 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 | |||
| 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 | 926 | int get_memory_ranges(struct memory_range **range, int *ranges, | |
| 912 | static 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' contains | 940 | /* 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 | else | 998 | } 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_OFFSET | 1038 | /* 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 | ||
| 998 | static 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 | |||
| 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 | |||
| 1031 | int arch_compat_trampoline(struct kexec_info *info) | 1066 | int arch_compat_trampoline(struct kexec_info *info) |
| 1032 | { | 1067 | { |
| 1033 | return 0; | 1068 | return 0; |