From 6a3871526c47ba73b1db91412ff95427cf00b795 Mon Sep 17 00:00:00 2001 From: chenhaixiang Date: Wed, 24 Aug 2022 16:40:49 +0800 Subject: [PATCH] arm64: fix PAGE_OFFSET calc for flipped mm Signed-off-by: chenhaixiang (cherry picked from commit b6f4a2181af6d6960c8a0c5ecb505f1543cdb298) --- ...ump-unify-routine-to-get-page_offset.patch | 113 +++++++++++++++ ...-fix-PAGE_OFFSET-calc-for-flipped-mm.patch | 89 ++++++++++++ arm64-make-phys_offset-signed.patch | 124 ++++++++++++++++ ...ITS-from-kcore-for-52-bits-VA-kernel.patch | 137 ++++++++++++++++++ kexec-tools.spec | 13 +- 5 files changed, 475 insertions(+), 1 deletion(-) create mode 100644 arm64-crashdump-unify-routine-to-get-page_offset.patch create mode 100644 arm64-fix-PAGE_OFFSET-calc-for-flipped-mm.patch create mode 100644 arm64-make-phys_offset-signed.patch create mode 100644 arm64-read-VA_BITS-from-kcore-for-52-bits-VA-kernel.patch diff --git a/arm64-crashdump-unify-routine-to-get-page_offset.patch b/arm64-crashdump-unify-routine-to-get-page_offset.patch new file mode 100644 index 0000000..c0aab4a --- /dev/null +++ b/arm64-crashdump-unify-routine-to-get-page_offset.patch @@ -0,0 +1,113 @@ +From bde864387a104137ff3bd5f0871709846d5c7943 Mon Sep 17 00:00:00 2001 +From: Pingfan Liu +Date: Tue, 18 Jan 2022 15:48:10 +0800 +Subject: [PATCH] arm64/crashdump: unify routine to get page_offset + +There are two funcs to get page_offset: + get_kernel_page_offset() + get_page_offset() + +Since get_kernel_page_offset() does not observe the kernel formula, and +remove it. Unify them in order to introduce 52-bits VA kernel more +easily in the coming patch. + +Signed-off-by: Pingfan Liu +Reviewed-by: Philipp Rudo +Signed-off-by: Simon Horman +Conflict:NA +Reference:https://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git/commit/?id=bde864387a104137ff3bd5f0871709846d5c7943 +--- + kexec/arch/arm64/crashdump-arm64.c | 23 +---------------------- + kexec/arch/arm64/kexec-arm64.c | 8 ++++---- + kexec/arch/arm64/kexec-arm64.h | 1 + + 3 files changed, 6 insertions(+), 26 deletions(-) + +diff --git a/kexec/arch/arm64/crashdump-arm64.c b/kexec/arch/arm64/crashdump-arm64.c +index d0f2253..7beb1fb 100644 +--- a/kexec/arch/arm64/crashdump-arm64.c ++++ b/kexec/arch/arm64/crashdump-arm64.c +@@ -46,27 +46,6 @@ static struct crash_elf_info elf_info = { + .machine = EM_AARCH64, + }; + +-/* +- * Note: The returned value is correct only if !CONFIG_RANDOMIZE_BASE. +- */ +-static uint64_t get_kernel_page_offset(void) +-{ +- int i; +- +- if (elf_info.kern_vaddr_start == UINT64_MAX) +- return UINT64_MAX; +- +- /* Current max virtual memory range is 48-bits. */ +- for (i = 48; i > 0; i--) +- if (!(elf_info.kern_vaddr_start & (1UL << i))) +- break; +- +- if (i <= 0) +- return UINT64_MAX; +- else +- return UINT64_MAX << i; +-} +- + /* + * iomem_range_callback() - callback called for each iomem region + * @data: not used +@@ -203,7 +182,7 @@ int load_crashdump_segments(struct kexec_info *info) + if (err) + return EFAILED; + +- elf_info.page_offset = get_kernel_page_offset(); ++ get_page_offset(&elf_info.page_offset); + dbgprintf("%s: page_offset: %016llx\n", __func__, + elf_info.page_offset); + +diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c +index 9dc1d8f..0f8a768 100644 +--- a/kexec/arch/arm64/kexec-arm64.c ++++ b/kexec/arch/arm64/kexec-arm64.c +@@ -928,7 +928,7 @@ static int get_va_bits(void) + * get_page_offset - Helper for getting PAGE_OFFSET + */ + +-static int get_page_offset(void) ++int get_page_offset(unsigned long *page_offset) + { + int ret; + +@@ -936,8 +936,8 @@ static int get_page_offset(void) + if (ret < 0) + return ret; + +- page_offset = (0xffffffffffffffffUL) << (va_bits - 1); +- dbgprintf("page_offset : %lx\n", page_offset); ++ *page_offset = UINT64_MAX << (va_bits - 1); ++ dbgprintf("page_offset : %lx\n", *page_offset); + + return 0; + } +@@ -973,7 +973,7 @@ int get_phys_base_from_pt_load(long *phys_offset) + unsigned long long phys_start; + unsigned long long virt_start; + +- ret = get_page_offset(); ++ ret = get_page_offset(&page_offset); + if (ret < 0) + return ret; + +diff --git a/kexec/arch/arm64/kexec-arm64.h b/kexec/arch/arm64/kexec-arm64.h +index bfd4172..5eb9fc0 100644 +--- a/kexec/arch/arm64/kexec-arm64.h ++++ b/kexec/arch/arm64/kexec-arm64.h +@@ -69,6 +69,7 @@ extern struct arm64_mem arm64_mem; + + uint64_t get_phys_offset(void); + uint64_t get_vp_offset(void); ++int get_page_offset(unsigned long *offset); + + static inline void reset_vp_offset(void) + { +-- +2.33.0 + diff --git a/arm64-fix-PAGE_OFFSET-calc-for-flipped-mm.patch b/arm64-fix-PAGE_OFFSET-calc-for-flipped-mm.patch new file mode 100644 index 0000000..52f4c12 --- /dev/null +++ b/arm64-fix-PAGE_OFFSET-calc-for-flipped-mm.patch @@ -0,0 +1,89 @@ +From 95de9eccf413ece6a86ff6b5a8e47f9b16b64454 Mon Sep 17 00:00:00 2001 +From: Kairui Song +Date: Tue, 18 Jan 2022 15:48:12 +0800 +Subject: [PATCH] arm64: fix PAGE_OFFSET calc for flipped mm + +Since kernel commit 14c127c957c1 ('arm64: mm: Flip kernel VA space'), +the memory layout on arm64 have changed, and kexec-tools can no longer +get the the right PAGE_OFFSET based on _text symbol. + +Prior to that, the kimage (_text) lays above PAGE_END with this layout: +0 -> VA_START : Usespace +VA_START -> VA_START + 256M : BPF JIT, Modules +VA_START + 256M -> PAGE_OFFSET - (~GB misc) : Vmalloc (KERNEL _text HERE) +PAGE_OFFSET -> ... : * Linear map * + +And here we have: +VA_START = -1UL << VA_BITS +PAGE_OFFSET = -1UL << (VA_BITS - 1) +_text < -1UL << (VA_BITS - 1) + +Kernel image lays somewhere between VA_START and PAGE_OFFSET, so we just +calc VA_BITS by getting the highest unset bit of _text symbol address, +and shift one less bit of VA_BITS to get page offset. This works as long +as KASLR don't put kernel in a too high location (which is commented inline). + +And after that commit, kernel layout have changed: +0 -> PAGE_OFFSET : Userspace +PAGE_OFFSET -> PAGE_END : * Linear map * +PAGE_END -> PAGE_END + 128M : bpf jit region +PAGE_END + 128M -> PAGE_END + 256MB : modules +PAGE_END + 256M -> ... : vmalloc (KERNEL _text HERE) + +Here we have: +PAGE_OFFSET = -1UL << VA_BITS +PAGE_END = -1UL << (VA_BITS - 1) +_text > -1UL << (VA_BITS - 1) + +Kernel image now lays above PAGE_END, so we have to shift one more bit to +get the VA_BITS, and shift the exact VA_BITS for PAGE_OFFSET. + +We can simply check if "_text > -1UL << (VA_BITS - 1)" is true to judge +which layout is being used and shift the page offset occordingly. + +Signed-off-by: Kairui Song +(rebased and stripped by Pingfan ) +Signed-off-by: Pingfan Liu +Reviewed-by: Philipp Rudo +Signed-off-by: Simon Horman +Conflict:NA +Reference:https://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git/commit/?id=95de9eccf413ece6a86ff6b5a8e47f9b16b64454 + +--- + kexec/arch/arm64/kexec-arm64.c | 14 +++++++++++++- + 1 file changed, 13 insertions(+), 1 deletion(-) + +diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c +index e502be0..9dd072c 100644 +--- a/kexec/arch/arm64/kexec-arm64.c ++++ b/kexec/arch/arm64/kexec-arm64.c +@@ -942,13 +942,25 @@ out: + + int get_page_offset(unsigned long *page_offset) + { ++ unsigned long long text_sym_addr, kernel_va_mid; + int ret; + ++ text_sym_addr = get_kernel_sym("_text"); ++ if (text_sym_addr == 0) { ++ fprintf(stderr, "Can't get the symbol of _text to calculate page_offset.\n"); ++ return -1; ++ } ++ + ret = get_va_bits(); + if (ret < 0) + return ret; + +- if (va_bits < 52) ++ /* Since kernel 5.4, kernel image is put above ++ * UINT64_MAX << (va_bits - 1) ++ */ ++ kernel_va_mid = UINT64_MAX << (va_bits - 1); ++ /* older kernel */ ++ if (text_sym_addr < kernel_va_mid) + *page_offset = UINT64_MAX << (va_bits - 1); + else + *page_offset = UINT64_MAX << va_bits; +-- +2.33.0 + diff --git a/arm64-make-phys_offset-signed.patch b/arm64-make-phys_offset-signed.patch new file mode 100644 index 0000000..856f925 --- /dev/null +++ b/arm64-make-phys_offset-signed.patch @@ -0,0 +1,124 @@ +From 67ea2d99e1356352034dc9d9c7b5ec6dd6b722eb Mon Sep 17 00:00:00 2001 +From: Pingfan Liu +Date: Tue, 18 Jan 2022 15:48:09 +0800 +Subject: [PATCH] arm64: make phys_offset signed + +After kernel commit 7bc1a0f9e176 ("arm64: mm: use single quantity to +represent the PA to VA translation"), phys_offset can be negative if +running 52-bits kernel on 48-bits hardware. + +So changing phys_offset from unsigned to signed. + +Signed-off-by: Pingfan Liu +Reviewed-by: Philipp Rudo +Signed-off-by: Simon Horman +Conflict:NA +Reference:https://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git/commit/?id=67ea2d99e1356352034dc9d9c7b5ec6dd6b722eb + +--- + kexec/arch/arm64/kexec-arm64.c | 12 ++++++------ + kexec/arch/arm64/kexec-arm64.h | 2 +- + util_lib/elf_info.c | 2 +- + util_lib/include/elf_info.h | 2 +- + 4 files changed, 9 insertions(+), 9 deletions(-) + +diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c +index 44ca3db..9dc1d8f 100644 +--- a/kexec/arch/arm64/kexec-arm64.c ++++ b/kexec/arch/arm64/kexec-arm64.c +@@ -878,7 +878,7 @@ void add_segment(struct kexec_info *info, const void *buf, size_t bufsz, + add_segment_phys_virt(info, buf, bufsz, base, memsz, 1); + } + +-static inline void set_phys_offset(uint64_t v, char *set_method) ++static inline void set_phys_offset(int64_t v, char *set_method) + { + if (arm64_mem.phys_offset == arm64_mem_ngv + || v < arm64_mem.phys_offset) { +@@ -947,7 +947,7 @@ static int get_page_offset(void) + * from VMCOREINFO note inside 'kcore'. + */ + +-static int get_phys_offset_from_vmcoreinfo_pt_note(unsigned long *phys_offset) ++static int get_phys_offset_from_vmcoreinfo_pt_note(long *phys_offset) + { + int fd, ret = 0; + +@@ -967,7 +967,7 @@ static int get_phys_offset_from_vmcoreinfo_pt_note(unsigned long *phys_offset) + * from PT_LOADs inside 'kcore'. + */ + +-int get_phys_base_from_pt_load(unsigned long *phys_offset) ++int get_phys_base_from_pt_load(long *phys_offset) + { + int i, fd, ret; + unsigned long long phys_start; +@@ -1025,7 +1025,7 @@ static bool to_be_excluded(char *str, unsigned long long start, unsigned long lo + int get_memory_ranges(struct memory_range **range, int *ranges, + unsigned long kexec_flags) + { +- unsigned long phys_offset = UINT64_MAX; ++ long phys_offset = -1; + FILE *fp; + const char *iomem = proc_iomem(); + char line[MAX_LINE], *str; +@@ -1047,7 +1047,7 @@ int get_memory_ranges(struct memory_range **range, int *ranges, + */ + ret = get_phys_offset_from_vmcoreinfo_pt_note(&phys_offset); + if (!ret) { +- if (phys_offset != UINT64_MAX) ++ if (phys_offset != -1) + set_phys_offset(phys_offset, + "vmcoreinfo pt_note"); + } else { +@@ -1059,7 +1059,7 @@ int get_memory_ranges(struct memory_range **range, int *ranges, + */ + ret = get_phys_base_from_pt_load(&phys_offset); + if (!ret) +- if (phys_offset != UINT64_MAX) ++ if (phys_offset != -1) + set_phys_offset(phys_offset, + "pt_load"); + } +diff --git a/kexec/arch/arm64/kexec-arm64.h b/kexec/arch/arm64/kexec-arm64.h +index ed447ac..bfd4172 100644 +--- a/kexec/arch/arm64/kexec-arm64.h ++++ b/kexec/arch/arm64/kexec-arm64.h +@@ -58,7 +58,7 @@ extern off_t initrd_size; + */ + + struct arm64_mem { +- uint64_t phys_offset; ++ int64_t phys_offset; + uint64_t text_offset; + uint64_t image_size; + uint64_t vp_offset; +diff --git a/util_lib/elf_info.c b/util_lib/elf_info.c +index 51d8b92..5574c7f 100644 +--- a/util_lib/elf_info.c ++++ b/util_lib/elf_info.c +@@ -1236,7 +1236,7 @@ int read_elf(int fd) + return 0; + } + +-int read_phys_offset_elf_kcore(int fd, unsigned long *phys_off) ++int read_phys_offset_elf_kcore(int fd, long *phys_off) + { + int ret; + +diff --git a/util_lib/include/elf_info.h b/util_lib/include/elf_info.h +index 4bc9279..f550d86 100644 +--- a/util_lib/include/elf_info.h ++++ b/util_lib/include/elf_info.h +@@ -28,7 +28,7 @@ int get_pt_load(int idx, + unsigned long long *phys_end, + unsigned long long *virt_start, + unsigned long long *virt_end); +-int read_phys_offset_elf_kcore(int fd, unsigned long *phys_off); ++int read_phys_offset_elf_kcore(int fd, long *phys_off); + int read_elf(int fd); + void dump_dmesg(int fd, void (*handler)(char*, unsigned int)); + +-- +2.33.0 + diff --git a/arm64-read-VA_BITS-from-kcore-for-52-bits-VA-kernel.patch b/arm64-read-VA_BITS-from-kcore-for-52-bits-VA-kernel.patch new file mode 100644 index 0000000..1e64cc1 --- /dev/null +++ b/arm64-read-VA_BITS-from-kcore-for-52-bits-VA-kernel.patch @@ -0,0 +1,137 @@ +From 454395e18ff12d2728ee458695160e9ab4899e33 Mon Sep 17 00:00:00 2001 +From: Pingfan Liu +Date: Tue, 18 Jan 2022 15:48:11 +0800 +Subject: [PATCH] arm64: read VA_BITS from kcore for 52-bits VA kernel + +phys_to_virt() calculates virtual address. As a important factor, +page_offset is excepted to be accurate. + +Since arm64 kernel exposes va_bits through vmcore, using it. + +Signed-off-by: Pingfan Liu +Reviewed-by: Philipp Rudo +Signed-off-by: Simon Horman +Conflict:NA +Reference:https://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git/commit/?id=454395e18ff12d2728ee458695160e9ab4899e33 +--- + kexec/arch/arm64/kexec-arm64.c | 34 ++++++++++++++++++++++++++++++---- + util_lib/elf_info.c | 5 +++++ + util_lib/include/elf_info.h | 1 + + 3 files changed, 36 insertions(+), 4 deletions(-) + +diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c +index 0f8a768..e502be0 100644 +--- a/kexec/arch/arm64/kexec-arm64.c ++++ b/kexec/arch/arm64/kexec-arm64.c +@@ -54,7 +54,7 @@ + static bool try_read_phys_offset_from_kcore = false; + + /* Machine specific details. */ +-static int va_bits; ++static int va_bits = -1; + static unsigned long page_offset; + + /* Global varables the core kexec routines expect. */ +@@ -895,7 +895,18 @@ static inline void set_phys_offset(int64_t v, char *set_method) + + static int get_va_bits(void) + { +- unsigned long long stext_sym_addr = get_kernel_sym("_stext"); ++ unsigned long long stext_sym_addr; ++ ++ /* ++ * if already got from kcore ++ */ ++ if (va_bits != -1) ++ goto out; ++ ++ ++ /* For kernel older than v4.19 */ ++ fprintf(stderr, "Warning, can't get the VA_BITS from kcore\n"); ++ stext_sym_addr = get_kernel_sym("_stext"); + + if (stext_sym_addr == 0) { + fprintf(stderr, "Can't get the symbol of _stext.\n"); +@@ -919,6 +930,7 @@ static int get_va_bits(void) + return -1; + } + ++out: + dbgprintf("va_bits : %d\n", va_bits); + + return 0; +@@ -936,14 +948,27 @@ int get_page_offset(unsigned long *page_offset) + if (ret < 0) + return ret; + +- *page_offset = UINT64_MAX << (va_bits - 1); ++ if (va_bits < 52) ++ *page_offset = UINT64_MAX << (va_bits - 1); ++ else ++ *page_offset = UINT64_MAX << va_bits; ++ + dbgprintf("page_offset : %lx\n", *page_offset); + + return 0; + } + ++static void arm64_scan_vmcoreinfo(char *pos) ++{ ++ const char *str; ++ ++ str = "NUMBER(VA_BITS)="; ++ if (memcmp(str, pos, strlen(str)) == 0) ++ va_bits = strtoul(pos + strlen(str), NULL, 10); ++} ++ + /** +- * get_phys_offset_from_vmcoreinfo_pt_note - Helper for getting PHYS_OFFSET ++ * get_phys_offset_from_vmcoreinfo_pt_note - Helper for getting PHYS_OFFSET (and va_bits) + * from VMCOREINFO note inside 'kcore'. + */ + +@@ -956,6 +981,7 @@ static int get_phys_offset_from_vmcoreinfo_pt_note(long *phys_offset) + return EFAILED; + } + ++ arch_scan_vmcoreinfo = arm64_scan_vmcoreinfo; + ret = read_phys_offset_elf_kcore(fd, phys_offset); + + close(fd); +diff --git a/util_lib/elf_info.c b/util_lib/elf_info.c +index 5574c7f..d252eff 100644 +--- a/util_lib/elf_info.c ++++ b/util_lib/elf_info.c +@@ -310,6 +310,8 @@ int get_pt_load(int idx, + + #define NOT_FOUND_LONG_VALUE (-1) + ++void (*arch_scan_vmcoreinfo)(char *pos); ++ + void scan_vmcoreinfo(char *start, size_t size) + { + char *last = start + size - 1; +@@ -551,6 +553,9 @@ void scan_vmcoreinfo(char *start, size_t size) + } + } + ++ if (arch_scan_vmcoreinfo != NULL) ++ (*arch_scan_vmcoreinfo)(pos); ++ + if (last_line) + break; + } +diff --git a/util_lib/include/elf_info.h b/util_lib/include/elf_info.h +index f550d86..fdf4c3d 100644 +--- a/util_lib/include/elf_info.h ++++ b/util_lib/include/elf_info.h +@@ -31,5 +31,6 @@ int get_pt_load(int idx, + int read_phys_offset_elf_kcore(int fd, long *phys_off); + int read_elf(int fd); + void dump_dmesg(int fd, void (*handler)(char*, unsigned int)); ++extern void (*arch_scan_vmcoreinfo)(char *pos); + + #endif /* ELF_INFO_H */ +-- +2.33.0 + diff --git a/kexec-tools.spec b/kexec-tools.spec index 29521ad..3c63b8d 100644 --- a/kexec-tools.spec +++ b/kexec-tools.spec @@ -4,7 +4,7 @@ Name: kexec-tools Version: 2.0.23 -Release: 6 +Release: 7 License: GPLv2 Summary: The kexec/kdump userspace component URL: https://www.kernel.org/ @@ -73,6 +73,10 @@ Patch0002: add-secure-compile-options-for-makedumpfile.patch Patch0003: kexec-Add-quick-kexec-support.patch Patch0004: kexec-Quick-kexec-implementation-for-arm64.patch Patch0005: arm64-crashdump-deduce-paddr-of-_text-based-on-kerne.patch +Patch0006: arm64-make-phys_offset-signed.patch +Patch0007: arm64-crashdump-unify-routine-to-get-page_offset.patch +Patch0008: arm64-read-VA_BITS-from-kcore-for-52-bits-VA-kernel.patch +Patch0009: arm64-fix-PAGE_OFFSET-calc-for-flipped-mm.patch %description kexec-tools provides /sbin/kexec binary that facilitates a new @@ -106,6 +110,10 @@ tar -z -x -v -f %{SOURCE19} %patch0004 -p1 %endif %patch0005 -p1 +%patch0006 -p1 +%patch0007 -p1 +%patch0008 -p1 +%patch0009 -p1 %build autoreconf @@ -290,6 +298,9 @@ done %endif %changelog +* Wed Aug 24 2022 chenhaixiang - 2.0.23-7 +- arm64: fix PAGE_OFFSET calc for flipped mm + * Tue Aug 23 2022 chenhaixiang - 2.0.23-6 - kdumpctl:ignore deprecated and invalid kdump config option -- Gitee