diff options
-rw-r--r-- | kexec/arch/arm64/kexec-arm64.c | 153 |
1 files changed, 94 insertions, 59 deletions
diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c index 6ad3b0a..45ebc54 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; |