diff -Nru kexec-tools-2.0.18/debian/changelog kexec-tools-2.0.18/debian/changelog --- kexec-tools-2.0.18/debian/changelog 2019-01-31 19:59:38.000000000 +0200 +++ kexec-tools-2.0.18/debian/changelog 2023-07-03 16:29:58.000000000 +0300 @@ -1,3 +1,15 @@ +kexec-tools (1:2.0.18-1ubuntu1.1) focal; urgency=medium + + * Support more than one crash kernel regions for arm64 (LP: #2024479) + - d/p/lp2024479-0001-util_lib-Add-functionality-to-read-elf-notes.patch + - d/p/lp2024479-0002-arm64-Add-support-to-read-PHYS_OFFSET-from-kcore-pt_.patch + - d/p/lp2024479-0003-arm64-kexec-allocate-memory-space-avoiding-reserved-.patch + - d/p/lp2024479-0004-kexec-add-variant-helper-functions-for-handling-memo.patch + - d/p/lp2024479-0005-arm64-kdump-deal-with-a-lot-of-resource-entries-in-p.patch + - d/p/lp2024479-0006-arm64-support-more-than-one-crash-kernel-regions.patch + + -- Ioanna Alifieraki Mon, 03 Jul 2023 16:29:58 +0300 + kexec-tools (1:2.0.18-1ubuntu1) disco; urgency=medium * Merge from Debian unstable. Remaining changes: diff -Nru kexec-tools-2.0.18/debian/patches/lp2024479-0001-util_lib-Add-functionality-to-read-elf-notes.patch kexec-tools-2.0.18/debian/patches/lp2024479-0001-util_lib-Add-functionality-to-read-elf-notes.patch --- kexec-tools-2.0.18/debian/patches/lp2024479-0001-util_lib-Add-functionality-to-read-elf-notes.patch 1970-01-01 02:00:00.000000000 +0200 +++ kexec-tools-2.0.18/debian/patches/lp2024479-0001-util_lib-Add-functionality-to-read-elf-notes.patch 2023-07-03 16:29:58.000000000 +0300 @@ -0,0 +1,1746 @@ +From a87bae7d4b44f915dc3a825acf098080770bef12 Mon Sep 17 00:00:00 2001 +From: Bhupesh Sharma +Origin: upstream, https://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git/commit?id=a87bae7d4b44f915dc3a825acf098080770bef12 +Bug-Ubuntu: https://bugs.launchpad.net/bugs/2024479 +Date: Thu, 20 Dec 2018 16:52:51 +0530 +Subject: [PATCH 1/6] util_lib: Add functionality to read elf notes + +'vmcore-dmesg.c' already implements functionality to read +'vmcoreinfo' from vmcore file. The same can be used in other +features as well (one of which is reading the elf notes from +'kcore' file), so there is merit in moving this to the utility +libraries (util_lib). + +Newer kernel versions (>= 4.19, with commit 23c85094fe1895caefdd +["proc/kcore: add vmcoreinfo note to /proc/kcore"], available), +have 'kcore' which now contains a new PT_NOTE which carries +the VMCOREINFO information. + +If the same is available, we can benefit by using it in 'kexec-tools'. +This is especially useful for architectures like arm64 as we can +get kernel symbols like 'PHYS_OFFSET' from the '/proc/kcore' itself +and use it to calculate 'phys_offset' before we make a call to +'set_phys_offset()'. + +For older kernels, we can try and determine the PHYS_OFFSET +value from PT_LOAD segments inside 'kcore' via some jugglery +of the correct virtual and physical address combinations. + +Subsequent patch(es) in this series will use the same feature +to read the 'kcore' file. + +This patch also makes some of the functions which were earlier +present in 'vmcore-dmesg.c' as non-static, so as to allow +future patches to use them as library functions. + +Also we add the capability to read 'NUMBER(PHYS_OFFSET)' from +vmcoreinfo to the already present 'scan_vmcoreinfo()' code. + +Future patches can look at reading more vmcoreinfo information +(for e.g. 'kaslr_offset()' for x86_64 and arm64) by using the +same framework. + +Signed-off-by: Bhupesh Sharma +Signed-off-by: Simon Horman +--- + util_lib/Makefile | 4 +- + util_lib/elf_info.c | 856 ++++++++++++++++++++++++++++++++++++ + util_lib/include/elf_info.h | 35 ++ + vmcore-dmesg/Makefile | 4 +- + vmcore-dmesg/vmcore-dmesg.c | 742 +------------------------------ + 5 files changed, 900 insertions(+), 741 deletions(-) + create mode 100644 util_lib/elf_info.c + create mode 100644 util_lib/include/elf_info.h + +--- a/util_lib/Makefile ++++ b/util_lib/Makefile +@@ -3,6 +3,7 @@ + # + UTIL_LIB_SRCS += + UTIL_LIB_SRCS += util_lib/compute_ip_checksum.c ++UTIL_LIB_SRCS += util_lib/elf_info.c + UTIL_LIB_SRCS += util_lib/sha256.c + UTIL_LIB_OBJS =$(call objify, $(UTIL_LIB_SRCS)) + UTIL_LIB_DEPS =$(call depify, $(UTIL_LIB_OBJS)) +@@ -11,7 +12,8 @@ UTIL_LIB = libutil.a + -include $(UTIL_LIB_DEPS) + + dist += util_lib/Makefile $(UTIL_LIB_SRCS) \ +- util_lib/include/sha256.h util_lib/include/ip_checksum.h ++ util_lib/include/elf_info.h util_lib/include/sha256.h \ ++ util_lib/include/ip_checksum.h + clean += $(UTIL_LIB_OBJS) $(UTIL_LIB_DEPS) $(UTIL_LIB) + + $(UTIL_LIB): CPPFLAGS += -I$(srcdir)/util_lib/include +--- /dev/null ++++ b/util_lib/elf_info.c +@@ -0,0 +1,856 @@ ++/* ++ * elf_info.c: Architecture independent code for parsing elf files. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation (version 2 of the License). ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++ ++/* The 32bit and 64bit note headers make it clear we don't care */ ++typedef Elf32_Nhdr Elf_Nhdr; ++ ++static const char *fname; ++static Elf64_Ehdr ehdr; ++static Elf64_Phdr *phdr; ++static int num_pt_loads; ++ ++static char osrelease[4096]; ++ ++static loff_t log_buf_vaddr; ++static loff_t log_end_vaddr; ++static loff_t log_buf_len_vaddr; ++static loff_t logged_chars_vaddr; ++ ++/* record format logs */ ++static loff_t log_first_idx_vaddr; ++static loff_t log_next_idx_vaddr; ++ ++/* struct printk_log (or older log) size */ ++static uint64_t log_sz; ++ ++/* struct printk_log (or older log) field offsets */ ++static uint64_t log_offset_ts_nsec = UINT64_MAX; ++static uint16_t log_offset_len = UINT16_MAX; ++static uint16_t log_offset_text_len = UINT16_MAX; ++ ++static uint64_t phys_offset = UINT64_MAX; ++ ++#if __BYTE_ORDER == __LITTLE_ENDIAN ++#define ELFDATANATIVE ELFDATA2LSB ++#elif __BYTE_ORDER == __BIG_ENDIAN ++#define ELFDATANATIVE ELFDATA2MSB ++#else ++#error "Unknown machine endian" ++#endif ++ ++static uint16_t file16_to_cpu(uint16_t val) ++{ ++ if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) ++ val = bswap_16(val); ++ return val; ++} ++ ++static uint32_t file32_to_cpu(uint32_t val) ++{ ++ if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) ++ val = bswap_32(val); ++ return val; ++} ++ ++static uint64_t file64_to_cpu(uint64_t val) ++{ ++ if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) ++ val = bswap_64(val); ++ return val; ++} ++ ++static uint64_t vaddr_to_offset(uint64_t vaddr) ++{ ++ /* Just hand the simple case where kexec gets ++ * the virtual address on the program headers right. ++ */ ++ ssize_t i; ++ for (i = 0; i < ehdr.e_phnum; i++) { ++ if (phdr[i].p_vaddr > vaddr) ++ continue; ++ if ((phdr[i].p_vaddr + phdr[i].p_memsz) <= vaddr) ++ continue; ++ return (vaddr - phdr[i].p_vaddr) + phdr[i].p_offset; ++ } ++ fprintf(stderr, "No program header covering vaddr 0x%llxfound kexec bug?\n", ++ (unsigned long long)vaddr); ++ exit(30); ++} ++ ++static unsigned machine_pointer_bits(void) ++{ ++ uint8_t bits = 0; ++ ++ /* Default to the size of the elf class */ ++ switch(ehdr.e_ident[EI_CLASS]) { ++ case ELFCLASS32: bits = 32; break; ++ case ELFCLASS64: bits = 64; break; ++ } ++ ++ /* Report the architectures pointer size */ ++ switch(ehdr.e_machine) { ++ case EM_386: bits = 32; break; ++ } ++ ++ return bits; ++} ++ ++void read_elf32(int fd) ++{ ++ Elf32_Ehdr ehdr32; ++ Elf32_Phdr *phdr32; ++ size_t phdrs32_size; ++ ssize_t ret, i; ++ ++ ret = pread(fd, &ehdr32, sizeof(ehdr32), 0); ++ if (ret != sizeof(ehdr32)) { ++ fprintf(stderr, "Read of Elf header from %s failed: %s\n", ++ fname, strerror(errno)); ++ exit(10); ++ } ++ ++ ehdr.e_type = file16_to_cpu(ehdr32.e_type); ++ ehdr.e_machine = file16_to_cpu(ehdr32.e_machine); ++ ehdr.e_version = file32_to_cpu(ehdr32.e_version); ++ ehdr.e_entry = file32_to_cpu(ehdr32.e_entry); ++ ehdr.e_phoff = file32_to_cpu(ehdr32.e_phoff); ++ ehdr.e_shoff = file32_to_cpu(ehdr32.e_shoff); ++ ehdr.e_flags = file32_to_cpu(ehdr32.e_flags); ++ ehdr.e_ehsize = file16_to_cpu(ehdr32.e_ehsize); ++ ehdr.e_phentsize = file16_to_cpu(ehdr32.e_phentsize); ++ ehdr.e_phnum = file16_to_cpu(ehdr32.e_phnum); ++ ehdr.e_shentsize = file16_to_cpu(ehdr32.e_shentsize); ++ ehdr.e_shnum = file16_to_cpu(ehdr32.e_shnum); ++ ehdr.e_shstrndx = file16_to_cpu(ehdr32.e_shstrndx); ++ ++ if (ehdr.e_version != EV_CURRENT) { ++ fprintf(stderr, "Bad Elf header version %u\n", ++ ehdr.e_version); ++ exit(11); ++ } ++ if (ehdr.e_phentsize != sizeof(Elf32_Phdr)) { ++ fprintf(stderr, "Bad Elf progra header size %u expected %zu\n", ++ ehdr.e_phentsize, sizeof(Elf32_Phdr)); ++ exit(12); ++ } ++ phdrs32_size = ehdr.e_phnum * sizeof(Elf32_Phdr); ++ phdr32 = calloc(ehdr.e_phnum, sizeof(Elf32_Phdr)); ++ if (!phdr32) { ++ fprintf(stderr, "Calloc of %u phdrs32 failed: %s\n", ++ ehdr.e_phnum, strerror(errno)); ++ exit(14); ++ } ++ phdr = calloc(ehdr.e_phnum, sizeof(Elf64_Phdr)); ++ if (!phdr) { ++ fprintf(stderr, "Calloc of %u phdrs failed: %s\n", ++ ehdr.e_phnum, strerror(errno)); ++ exit(15); ++ } ++ ret = pread(fd, phdr32, phdrs32_size, ehdr.e_phoff); ++ if (ret < 0 || (size_t)ret != phdrs32_size) { ++ fprintf(stderr, "Read of program header @ 0x%llu for %zu bytes failed: %s\n", ++ (unsigned long long)ehdr.e_phoff, phdrs32_size, strerror(errno)); ++ exit(16); ++ } ++ for (i = 0; i < ehdr.e_phnum; i++) { ++ phdr[i].p_type = file32_to_cpu(phdr32[i].p_type); ++ phdr[i].p_offset = file32_to_cpu(phdr32[i].p_offset); ++ phdr[i].p_vaddr = file32_to_cpu(phdr32[i].p_vaddr); ++ phdr[i].p_paddr = file32_to_cpu(phdr32[i].p_paddr); ++ phdr[i].p_filesz = file32_to_cpu(phdr32[i].p_filesz); ++ phdr[i].p_memsz = file32_to_cpu(phdr32[i].p_memsz); ++ phdr[i].p_flags = file32_to_cpu(phdr32[i].p_flags); ++ phdr[i].p_align = file32_to_cpu(phdr32[i].p_align); ++ ++ if (phdr[i].p_type == PT_LOAD) ++ num_pt_loads++; ++ } ++ free(phdr32); ++} ++ ++void read_elf64(int fd) ++{ ++ Elf64_Ehdr ehdr64; ++ Elf64_Phdr *phdr64; ++ size_t phdrs_size; ++ ssize_t ret, i; ++ ++ ret = pread(fd, &ehdr64, sizeof(ehdr64), 0); ++ if (ret < 0 || (size_t)ret != sizeof(ehdr)) { ++ fprintf(stderr, "Read of Elf header from %s failed: %s\n", ++ fname, strerror(errno)); ++ exit(10); ++ } ++ ++ ehdr.e_type = file16_to_cpu(ehdr64.e_type); ++ ehdr.e_machine = file16_to_cpu(ehdr64.e_machine); ++ ehdr.e_version = file32_to_cpu(ehdr64.e_version); ++ ehdr.e_entry = file64_to_cpu(ehdr64.e_entry); ++ ehdr.e_phoff = file64_to_cpu(ehdr64.e_phoff); ++ ehdr.e_shoff = file64_to_cpu(ehdr64.e_shoff); ++ ehdr.e_flags = file32_to_cpu(ehdr64.e_flags); ++ ehdr.e_ehsize = file16_to_cpu(ehdr64.e_ehsize); ++ ehdr.e_phentsize = file16_to_cpu(ehdr64.e_phentsize); ++ ehdr.e_phnum = file16_to_cpu(ehdr64.e_phnum); ++ ehdr.e_shentsize = file16_to_cpu(ehdr64.e_shentsize); ++ ehdr.e_shnum = file16_to_cpu(ehdr64.e_shnum); ++ ehdr.e_shstrndx = file16_to_cpu(ehdr64.e_shstrndx); ++ ++ if (ehdr.e_version != EV_CURRENT) { ++ fprintf(stderr, "Bad Elf header version %u\n", ++ ehdr.e_version); ++ exit(11); ++ } ++ if (ehdr.e_phentsize != sizeof(Elf64_Phdr)) { ++ fprintf(stderr, "Bad Elf progra header size %u expected %zu\n", ++ ehdr.e_phentsize, sizeof(Elf64_Phdr)); ++ exit(12); ++ } ++ phdrs_size = ehdr.e_phnum * sizeof(Elf64_Phdr); ++ phdr64 = calloc(ehdr.e_phnum, sizeof(Elf64_Phdr)); ++ if (!phdr64) { ++ fprintf(stderr, "Calloc of %u phdrs64 failed: %s\n", ++ ehdr.e_phnum, strerror(errno)); ++ exit(14); ++ } ++ phdr = calloc(ehdr.e_phnum, sizeof(Elf64_Phdr)); ++ if (!phdr) { ++ fprintf(stderr, "Calloc of %u phdrs failed: %s\n", ++ ehdr.e_phnum, strerror(errno)); ++ exit(15); ++ } ++ ret = pread(fd, phdr64, phdrs_size, ehdr.e_phoff); ++ if (ret < 0 || (size_t)ret != phdrs_size) { ++ fprintf(stderr, "Read of program header @ %llu for %zu bytes failed: %s\n", ++ (unsigned long long)(ehdr.e_phoff), phdrs_size, strerror(errno)); ++ exit(16); ++ } ++ for (i = 0; i < ehdr.e_phnum; i++) { ++ phdr[i].p_type = file32_to_cpu(phdr64[i].p_type); ++ phdr[i].p_flags = file32_to_cpu(phdr64[i].p_flags); ++ phdr[i].p_offset = file64_to_cpu(phdr64[i].p_offset); ++ phdr[i].p_vaddr = file64_to_cpu(phdr64[i].p_vaddr); ++ phdr[i].p_paddr = file64_to_cpu(phdr64[i].p_paddr); ++ phdr[i].p_filesz = file64_to_cpu(phdr64[i].p_filesz); ++ phdr[i].p_memsz = file64_to_cpu(phdr64[i].p_memsz); ++ phdr[i].p_align = file64_to_cpu(phdr64[i].p_align); ++ ++ if (phdr[i].p_type == PT_LOAD) ++ num_pt_loads++; ++ } ++ free(phdr64); ++} ++ ++int get_pt_load(int idx, ++ unsigned long long *phys_start, ++ unsigned long long *phys_end, ++ unsigned long long *virt_start, ++ unsigned long long *virt_end) ++{ ++ Elf64_Phdr *pls; ++ ++ if (num_pt_loads <= idx) ++ return 0; ++ ++ pls = &phdr[idx]; ++ ++ if (phys_start) ++ *phys_start = pls->p_paddr; ++ if (phys_end) ++ *phys_end = pls->p_paddr + pls->p_memsz; ++ if (virt_start) ++ *virt_start = pls->p_vaddr; ++ if (virt_end) ++ *virt_end = pls->p_vaddr + pls->p_memsz; ++ ++ return 1; ++} ++ ++#define NOT_FOUND_LONG_VALUE (-1) ++ ++void scan_vmcoreinfo(char *start, size_t size) ++{ ++ char *last = start + size - 1; ++ char *pos, *eol; ++ char temp_buf[1024]; ++ bool last_line = false; ++ char *str, *endp; ++ ++#define SYMBOL(sym) { \ ++ .str = "SYMBOL(" #sym ")=", \ ++ .name = #sym, \ ++ .len = sizeof("SYMBOL(" #sym ")=") - 1, \ ++ .vaddr = & sym ## _vaddr, \ ++ } ++ static struct symbol { ++ const char *str; ++ const char *name; ++ size_t len; ++ loff_t *vaddr; ++ } symbol[] = { ++ SYMBOL(log_buf), ++ SYMBOL(log_end), ++ SYMBOL(log_buf_len), ++ SYMBOL(logged_chars), ++ SYMBOL(log_first_idx), ++ SYMBOL(log_next_idx), ++ }; ++ ++ for (pos = start; pos <= last; pos = eol + 1) { ++ size_t len, i; ++ /* Find the end of the current line */ ++ for (eol = pos; (eol <= last) && (*eol != '\n') ; eol++) ++ ; ++ if (eol > last) { ++ /* ++ * We did not find \n and note ended. Currently kernel ++ * is appending last field CRASH_TIME without \n. It ++ * is ugly but handle it. ++ */ ++ eol = last; ++ len = eol - pos + 1; ++ if (len >= sizeof(temp_buf)) ++ len = sizeof(temp_buf) - 1; ++ strncpy(temp_buf, pos, len); ++ temp_buf[len + 1] = '\0'; ++ ++ pos = temp_buf; ++ len = len + 1; ++ eol = pos + len -1; ++ last_line = true; ++ } else { ++ len = eol - pos + 1; ++ } ++ ++ /* Stomp the last character so I am guaranteed a terminating null */ ++ *eol = '\0'; ++ /* Copy OSRELEASE if I see it */ ++ if ((len >= 10) && (memcmp("OSRELEASE=", pos, 10) == 0)) { ++ size_t to_copy = len - 10; ++ if (to_copy >= sizeof(osrelease)) ++ to_copy = sizeof(osrelease) - 1; ++ memcpy(osrelease, pos + 10, to_copy); ++ osrelease[to_copy] = '\0'; ++ } ++ /* See if the line is mentions a symbol I am looking for */ ++ for (i = 0; i < sizeof(symbol)/sizeof(symbol[0]); i++ ) { ++ unsigned long long vaddr; ++ if (symbol[i].len >= len) ++ continue; ++ if (memcmp(symbol[i].str, pos, symbol[i].len) != 0) ++ continue; ++ /* Found a symbol now decode it */ ++ vaddr = strtoull(pos + symbol[i].len, NULL, 16); ++ /* Remember the virtual address */ ++ *symbol[i].vaddr = vaddr; ++ } ++ ++ /* Check for "SIZE(printk_log)" or older "SIZE(log)=" */ ++ str = "SIZE(log)="; ++ if (memcmp(str, pos, strlen(str)) == 0) ++ log_sz = strtoull(pos + strlen(str), NULL, 10); ++ ++ str = "SIZE(printk_log)="; ++ if (memcmp(str, pos, strlen(str)) == 0) ++ log_sz = strtoull(pos + strlen(str), NULL, 10); ++ ++ /* Check for struct printk_log (or older log) field offsets */ ++ str = "OFFSET(log.ts_nsec)="; ++ if (memcmp(str, pos, strlen(str)) == 0) ++ log_offset_ts_nsec = strtoull(pos + strlen(str), NULL, ++ 10); ++ str = "OFFSET(printk_log.ts_nsec)="; ++ if (memcmp(str, pos, strlen(str)) == 0) ++ log_offset_ts_nsec = strtoull(pos + strlen(str), NULL, ++ 10); ++ ++ str = "OFFSET(log.len)="; ++ if (memcmp(str, pos, strlen(str)) == 0) ++ log_offset_len = strtoul(pos + strlen(str), NULL, 10); ++ ++ str = "OFFSET(printk_log.len)="; ++ if (memcmp(str, pos, strlen(str)) == 0) ++ log_offset_len = strtoul(pos + strlen(str), NULL, 10); ++ ++ str = "OFFSET(log.text_len)="; ++ if (memcmp(str, pos, strlen(str)) == 0) ++ log_offset_text_len = strtoul(pos + strlen(str), NULL, ++ 10); ++ str = "OFFSET(printk_log.text_len)="; ++ if (memcmp(str, pos, strlen(str)) == 0) ++ log_offset_text_len = strtoul(pos + strlen(str), NULL, ++ 10); ++ ++ /* Check for PHYS_OFFSET number */ ++ str = "NUMBER(PHYS_OFFSET)="; ++ if (memcmp(str, pos, strlen(str)) == 0) { ++ phys_offset = strtoul(pos + strlen(str), &endp, ++ 10); ++ if (strlen(endp) != 0) ++ phys_offset = strtoul(pos + strlen(str), &endp, 16); ++ if ((phys_offset == LONG_MAX) || strlen(endp) != 0) { ++ fprintf(stderr, "Invalid data %s\n", ++ pos); ++ break; ++ } ++ } ++ ++ if (last_line) ++ break; ++ } ++} ++ ++static int scan_notes(int fd, loff_t start, loff_t lsize) ++{ ++ char *buf, *last, *note, *next; ++ size_t size; ++ ssize_t ret; ++ ++ if (lsize > SSIZE_MAX) { ++ fprintf(stderr, "Unable to handle note section of %llu bytes\n", ++ (unsigned long long)lsize); ++ exit(20); ++ } ++ ++ size = lsize; ++ buf = malloc(size); ++ if (!buf) { ++ fprintf(stderr, "Cannot malloc %zu bytes\n", size); ++ exit(21); ++ } ++ last = buf + size - 1; ++ ret = pread(fd, buf, size, start); ++ if (ret != (ssize_t)size) { ++ fprintf(stderr, "Cannot read note section @ 0x%llx of %zu bytes: %s\n", ++ (unsigned long long)start, size, strerror(errno)); ++ exit(22); ++ } ++ ++ for (note = buf; (note + sizeof(Elf_Nhdr)) < last; note = next) ++ { ++ Elf_Nhdr *hdr; ++ char *n_name, *n_desc; ++ size_t n_namesz, n_descsz, n_type; ++ ++ hdr = (Elf_Nhdr *)note; ++ n_namesz = file32_to_cpu(hdr->n_namesz); ++ n_descsz = file32_to_cpu(hdr->n_descsz); ++ n_type = file32_to_cpu(hdr->n_type); ++ ++ n_name = note + sizeof(*hdr); ++ n_desc = n_name + ((n_namesz + 3) & ~3); ++ next = n_desc + ((n_descsz + 3) & ~3); ++ ++ if (next > (last + 1)) ++ break; ++ ++ if ((memcmp(n_name, "VMCOREINFO", 11) != 0) || (n_type != 0)) ++ continue; ++ scan_vmcoreinfo(n_desc, n_descsz); ++ } ++ ++ if ((note + sizeof(Elf_Nhdr)) == last) ++ return -1; ++ ++ free(buf); ++ ++ return 0; ++} ++ ++static int scan_note_headers(int fd) ++{ ++ int i, ret = 0; ++ ++ for (i = 0; i < ehdr.e_phnum; i++) { ++ if (phdr[i].p_type != PT_NOTE) ++ continue; ++ ret = scan_notes(fd, phdr[i].p_offset, phdr[i].p_filesz); ++ } ++ ++ return ret; ++} ++ ++static uint64_t read_file_pointer(int fd, uint64_t addr) ++{ ++ uint64_t result; ++ ssize_t ret; ++ ++ if (machine_pointer_bits() == 64) { ++ uint64_t scratch; ++ ret = pread(fd, &scratch, sizeof(scratch), addr); ++ if (ret != sizeof(scratch)) { ++ fprintf(stderr, "Failed to read pointer @ 0x%llx: %s\n", ++ (unsigned long long)addr, strerror(errno)); ++ exit(40); ++ } ++ result = file64_to_cpu(scratch); ++ } else { ++ uint32_t scratch; ++ ret = pread(fd, &scratch, sizeof(scratch), addr); ++ if (ret != sizeof(scratch)) { ++ fprintf(stderr, "Failed to read pointer @ 0x%llx: %s\n", ++ (unsigned long long)addr, strerror(errno)); ++ exit(40); ++ } ++ result = file32_to_cpu(scratch); ++ } ++ return result; ++} ++ ++static uint32_t read_file_u32(int fd, uint64_t addr) ++{ ++ uint32_t scratch; ++ ssize_t ret; ++ ret = pread(fd, &scratch, sizeof(scratch), addr); ++ if (ret != sizeof(scratch)) { ++ fprintf(stderr, "Failed to read value @ 0x%llx: %s\n", ++ (unsigned long long)addr, strerror(errno)); ++ exit(41); ++ } ++ return file32_to_cpu(scratch); ++} ++ ++static int32_t read_file_s32(int fd, uint64_t addr) ++{ ++ return read_file_u32(fd, addr); ++} ++ ++static void write_to_stdout(char *buf, unsigned int nr) ++{ ++ ssize_t ret; ++ ++ ret = write(STDOUT_FILENO, buf, nr); ++ if (ret != nr) { ++ fprintf(stderr, "Failed to write out the dmesg log buffer!:" ++ " %s\n", strerror(errno)); ++ exit(54); ++ } ++} ++ ++static void dump_dmesg_legacy(int fd) ++{ ++ uint64_t log_buf, log_buf_offset; ++ unsigned log_end, logged_chars, log_end_wrapped; ++ int log_buf_len, to_wrap; ++ char *buf; ++ ssize_t ret; ++ ++ if (!log_buf_vaddr) { ++ fprintf(stderr, "Missing the log_buf symbol\n"); ++ exit(50); ++ } ++ if (!log_end_vaddr) { ++ fprintf(stderr, "Missing the log_end symbol\n"); ++ exit(51); ++ } ++ if (!log_buf_len_vaddr) { ++ fprintf(stderr, "Missing the log_bug_len symbol\n"); ++ exit(52); ++ } ++ if (!logged_chars_vaddr) { ++ fprintf(stderr, "Missing the logged_chars symbol\n"); ++ exit(53); ++ } ++ ++ log_buf = read_file_pointer(fd, vaddr_to_offset(log_buf_vaddr)); ++ log_end = read_file_u32(fd, vaddr_to_offset(log_end_vaddr)); ++ log_buf_len = read_file_s32(fd, vaddr_to_offset(log_buf_len_vaddr)); ++ logged_chars = read_file_u32(fd, vaddr_to_offset(logged_chars_vaddr)); ++ ++ log_buf_offset = vaddr_to_offset(log_buf); ++ ++ buf = calloc(1, log_buf_len); ++ if (!buf) { ++ fprintf(stderr, "Failed to malloc %d bytes for the logbuf: %s\n", ++ log_buf_len, strerror(errno)); ++ exit(51); ++ } ++ ++ log_end_wrapped = log_end % log_buf_len; ++ to_wrap = log_buf_len - log_end_wrapped; ++ ++ ret = pread(fd, buf, to_wrap, log_buf_offset + log_end_wrapped); ++ if (ret != to_wrap) { ++ fprintf(stderr, "Failed to read the first half of the log buffer: %s\n", ++ strerror(errno)); ++ exit(52); ++ } ++ ret = pread(fd, buf + to_wrap, log_end_wrapped, log_buf_offset); ++ if (ret != log_end_wrapped) { ++ fprintf(stderr, "Faield to read the second half of the log buffer: %s\n", ++ strerror(errno)); ++ exit(53); ++ } ++ ++ /* ++ * To collect full dmesg including the part before `dmesg -c` is useful ++ * for later debugging. Use same logic as what crash utility is using. ++ */ ++ logged_chars = log_end < log_buf_len ? log_end : log_buf_len; ++ ++ write_to_stdout(buf + (log_buf_len - logged_chars), logged_chars); ++} ++ ++static inline uint16_t struct_val_u16(char *ptr, unsigned int offset) ++{ ++ return(file16_to_cpu(*(uint16_t *)(ptr + offset))); ++} ++ ++static inline uint32_t struct_val_u32(char *ptr, unsigned int offset) ++{ ++ return(file32_to_cpu(*(uint32_t *)(ptr + offset))); ++} ++ ++static inline uint64_t struct_val_u64(char *ptr, unsigned int offset) ++{ ++ return(file64_to_cpu(*(uint64_t *)(ptr + offset))); ++} ++ ++/* Read headers of log records and dump accordingly */ ++static void dump_dmesg_structured(int fd) ++{ ++#define OUT_BUF_SIZE 4096 ++ uint64_t log_buf, log_buf_offset, ts_nsec; ++ uint32_t log_first_idx, log_next_idx, current_idx, len = 0, i; ++ char *buf, out_buf[OUT_BUF_SIZE]; ++ ssize_t ret; ++ char *msg; ++ uint16_t text_len; ++ imaxdiv_t imaxdiv_sec, imaxdiv_usec; ++ ++ if (!log_buf_vaddr) { ++ fprintf(stderr, "Missing the log_buf symbol\n"); ++ exit(60); ++ } ++ ++ if (!log_buf_len_vaddr) { ++ fprintf(stderr, "Missing the log_bug_len symbol\n"); ++ exit(61); ++ } ++ ++ if (!log_first_idx_vaddr) { ++ fprintf(stderr, "Missing the log_first_idx symbol\n"); ++ exit(62); ++ } ++ ++ if (!log_next_idx_vaddr) { ++ fprintf(stderr, "Missing the log_next_idx symbol\n"); ++ exit(63); ++ } ++ ++ if (!log_sz) { ++ fprintf(stderr, "Missing the struct log size export\n"); ++ exit(64); ++ } ++ ++ if (log_offset_ts_nsec == UINT64_MAX) { ++ fprintf(stderr, "Missing the log.ts_nsec offset export\n"); ++ exit(65); ++ } ++ ++ if (log_offset_len == UINT16_MAX) { ++ fprintf(stderr, "Missing the log.len offset export\n"); ++ exit(66); ++ } ++ ++ if (log_offset_text_len == UINT16_MAX) { ++ fprintf(stderr, "Missing the log.text_len offset export\n"); ++ exit(67); ++ } ++ ++ log_buf = read_file_pointer(fd, vaddr_to_offset(log_buf_vaddr)); ++ ++ log_first_idx = read_file_u32(fd, vaddr_to_offset(log_first_idx_vaddr)); ++ log_next_idx = read_file_u32(fd, vaddr_to_offset(log_next_idx_vaddr)); ++ ++ log_buf_offset = vaddr_to_offset(log_buf); ++ ++ buf = calloc(1, log_sz); ++ if (!buf) { ++ fprintf(stderr, "Failed to malloc %" PRId64 " bytes for the log:" ++ " %s\n", log_sz, strerror(errno)); ++ exit(64); ++ } ++ ++ /* Parse records and write out data at standard output */ ++ ++ current_idx = log_first_idx; ++ len = 0; ++ while (current_idx != log_next_idx) { ++ uint16_t loglen; ++ ++ ret = pread(fd, buf, log_sz, log_buf_offset + current_idx); ++ if (ret != log_sz) { ++ fprintf(stderr, "Failed to read log of size %" PRId64 " bytes:" ++ " %s\n", log_sz, strerror(errno)); ++ exit(65); ++ } ++ ts_nsec = struct_val_u64(buf, log_offset_ts_nsec); ++ imaxdiv_sec = imaxdiv(ts_nsec, 1000000000); ++ imaxdiv_usec = imaxdiv(imaxdiv_sec.rem, 1000); ++ ++ len += sprintf(out_buf + len, "[%5llu.%06llu] ", ++ (long long unsigned int)imaxdiv_sec.quot, ++ (long long unsigned int)imaxdiv_usec.quot); ++ ++ /* escape non-printable characters */ ++ text_len = struct_val_u16(buf, log_offset_text_len); ++ msg = calloc(1, text_len); ++ if (!msg) { ++ fprintf(stderr, "Failed to malloc %u bytes for log text:" ++ " %s\n", text_len, strerror(errno)); ++ exit(64); ++ } ++ ++ ret = pread(fd, msg, text_len, log_buf_offset + current_idx + log_sz); ++ if (ret != text_len) { ++ fprintf(stderr, "Failed to read log text of size %u bytes:" ++ " %s\n", text_len, strerror(errno)); ++ exit(65); ++ } ++ for (i = 0; i < text_len; i++) { ++ unsigned char c = msg[i]; ++ ++ if (!isprint(c) && !isspace(c)) ++ len += sprintf(out_buf + len, "\\x%02x", c); ++ else ++ out_buf[len++] = c; ++ ++ if (len >= OUT_BUF_SIZE - 64) { ++ write_to_stdout(out_buf, len); ++ len = 0; ++ } ++ } ++ ++ out_buf[len++] = '\n'; ++ free(msg); ++ /* ++ * A length == 0 record is the end of buffer marker. Wrap around ++ * and read the message at the start of the buffer. ++ */ ++ loglen = struct_val_u16(buf, log_offset_len); ++ if (!loglen) ++ current_idx = 0; ++ else ++ /* Move to next record */ ++ current_idx += loglen; ++ } ++ free(buf); ++ if (len) ++ write_to_stdout(out_buf, len); ++} ++ ++static void dump_dmesg(int fd) ++{ ++ if (log_first_idx_vaddr) ++ dump_dmesg_structured(fd); ++ else ++ dump_dmesg_legacy(fd); ++} ++ ++static int read_elf(int fd) ++{ ++ int ret; ++ ++ ret = pread(fd, ehdr.e_ident, EI_NIDENT, 0); ++ if (ret != EI_NIDENT) { ++ fprintf(stderr, "Read of e_ident from %s failed: %s\n", ++ fname, strerror(errno)); ++ return 3; ++ } ++ if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0) { ++ fprintf(stderr, "Missing elf signature\n"); ++ return 4; ++ } ++ if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) { ++ fprintf(stderr, "Bad elf version\n"); ++ return 5; ++ } ++ if ((ehdr.e_ident[EI_CLASS] != ELFCLASS32) && ++ (ehdr.e_ident[EI_CLASS] != ELFCLASS64)) ++ { ++ fprintf(stderr, "Unknown elf class %u\n", ++ ehdr.e_ident[EI_CLASS]); ++ return 6; ++ } ++ if ((ehdr.e_ident[EI_DATA] != ELFDATA2LSB) && ++ (ehdr.e_ident[EI_DATA] != ELFDATA2MSB)) ++ { ++ fprintf(stderr, "Unkown elf data order %u\n", ++ ehdr.e_ident[EI_DATA]); ++ return 7; ++ } ++ if (ehdr.e_ident[EI_CLASS] == ELFCLASS32) ++ read_elf32(fd); ++ else ++ read_elf64(fd); ++ ++ ret = scan_note_headers(fd); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++int read_elf_vmcore(int fd) ++{ ++ int ret; ++ ++ ret = read_elf(fd); ++ if (ret > 0) { ++ fprintf(stderr, "Unable to read ELF information" ++ " from vmcore\n"); ++ return ret; ++ } ++ ++ dump_dmesg(fd); ++ ++ return 0; ++} ++ ++int read_elf_kcore(int fd) ++{ ++ int ret; ++ ++ ret = read_elf(fd); ++ if (ret != 0) ++ return ret; ++ ++ return 0; ++} ++ ++int read_phys_offset_elf_kcore(int fd, unsigned long *phys_off) ++{ ++ int ret; ++ ++ *phys_off = UINT64_MAX; ++ ++ ret = read_elf_kcore(fd); ++ if (!ret) { ++ /* If we have a valid 'PHYS_OFFSET' by now, ++ * return it to the caller now. ++ */ ++ if (phys_offset != UINT64_MAX) { ++ *phys_off = phys_offset; ++ return ret; ++ } ++ } ++ ++ return 2; ++} +--- /dev/null ++++ b/util_lib/include/elf_info.h +@@ -0,0 +1,35 @@ ++#ifndef ELF_INFO_H ++#define ELF_INFO_H ++ ++#define _XOPEN_SOURCE 700 ++#define _GNU_SOURCE ++#define _LARGEFILE_SOURCE 1 ++#define _FILE_OFFSET_BITS 64 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++int get_pt_load(int idx, ++ unsigned long long *phys_start, ++ 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_elf_kcore(int fd); ++int read_elf_vmcore(int fd); ++ ++#endif /* ELF_INFO_H */ +--- a/vmcore-dmesg/Makefile ++++ b/vmcore-dmesg/Makefile +@@ -15,9 +15,9 @@ clean += $(VMCORE_DMESG_OBJS) $(VMCORE_D + + -include $(VMCORE_DMESG_DEPS) + +-$(VMCORE_DMESG): $(VMCORE_DMESG_OBJS) ++$(VMCORE_DMESG): $(VMCORE_DMESG_OBJS) $(UTIL_LIB) + @$(MKDIR) -p $(@D) +- $(LINK.o) -o $@ $^ $(CFLAGS) ++ $(LINK.o) -o $@ $^ $(CFLAGS) $(LIBS) + + $(VMCORE_DMESG_MANPAGE): vmcore-dmesg/vmcore-dmesg.8 + $(MKDIR) -p $(MANDIR)/man8 +--- a/vmcore-dmesg/vmcore-dmesg.c ++++ b/vmcore-dmesg/vmcore-dmesg.c +@@ -1,711 +1,9 @@ +-#define _XOPEN_SOURCE 600 +-#define _GNU_SOURCE +-#define _LARGEFILE_SOURCE 1 +-#define _FILE_OFFSET_BITS 64 +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include ++#include + + /* The 32bit and 64bit note headers make it clear we don't care */ + typedef Elf32_Nhdr Elf_Nhdr; + + static const char *fname; +-static Elf64_Ehdr ehdr; +-static Elf64_Phdr *phdr; +- +-static char osrelease[4096]; +-static loff_t log_buf_vaddr; +-static loff_t log_end_vaddr; +-static loff_t log_buf_len_vaddr; +-static loff_t logged_chars_vaddr; +- +-/* record format logs */ +-static loff_t log_first_idx_vaddr; +-static loff_t log_next_idx_vaddr; +- +-/* struct printk_log (or older log) size */ +-static uint64_t log_sz; +- +-/* struct printk_log (or older log) field offsets */ +-static uint64_t log_offset_ts_nsec = UINT64_MAX; +-static uint16_t log_offset_len = UINT16_MAX; +-static uint16_t log_offset_text_len = UINT16_MAX; +- +-#if __BYTE_ORDER == __LITTLE_ENDIAN +-#define ELFDATANATIVE ELFDATA2LSB +-#elif __BYTE_ORDER == __BIG_ENDIAN +-#define ELFDATANATIVE ELFDATA2MSB +-#else +-#error "Unknown machine endian" +-#endif +- +-static uint16_t file16_to_cpu(uint16_t val) +-{ +- if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) +- val = bswap_16(val); +- return val; +-} +- +-static uint32_t file32_to_cpu(uint32_t val) +-{ +- if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) +- val = bswap_32(val); +- return val; +-} +- +-static uint64_t file64_to_cpu(uint64_t val) +-{ +- if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) +- val = bswap_64(val); +- return val; +-} +- +-static uint64_t vaddr_to_offset(uint64_t vaddr) +-{ +- /* Just hand the simple case where kexec gets +- * the virtual address on the program headers right. +- */ +- ssize_t i; +- for (i = 0; i < ehdr.e_phnum; i++) { +- if (phdr[i].p_vaddr > vaddr) +- continue; +- if ((phdr[i].p_vaddr + phdr[i].p_memsz) <= vaddr) +- continue; +- return (vaddr - phdr[i].p_vaddr) + phdr[i].p_offset; +- } +- fprintf(stderr, "No program header covering vaddr 0x%llxfound kexec bug?\n", +- (unsigned long long)vaddr); +- exit(30); +-} +- +-static unsigned machine_pointer_bits(void) +-{ +- uint8_t bits = 0; +- +- /* Default to the size of the elf class */ +- switch(ehdr.e_ident[EI_CLASS]) { +- case ELFCLASS32: bits = 32; break; +- case ELFCLASS64: bits = 64; break; +- } +- +- /* Report the architectures pointer size */ +- switch(ehdr.e_machine) { +- case EM_386: bits = 32; break; +- } +- +- return bits; +-} +- +-static void read_elf32(int fd) +-{ +- Elf32_Ehdr ehdr32; +- Elf32_Phdr *phdr32; +- size_t phdrs32_size; +- ssize_t ret, i; +- +- ret = pread(fd, &ehdr32, sizeof(ehdr32), 0); +- if (ret != sizeof(ehdr32)) { +- fprintf(stderr, "Read of Elf header from %s failed: %s\n", +- fname, strerror(errno)); +- exit(10); +- } +- +- ehdr.e_type = file16_to_cpu(ehdr32.e_type); +- ehdr.e_machine = file16_to_cpu(ehdr32.e_machine); +- ehdr.e_version = file32_to_cpu(ehdr32.e_version); +- ehdr.e_entry = file32_to_cpu(ehdr32.e_entry); +- ehdr.e_phoff = file32_to_cpu(ehdr32.e_phoff); +- ehdr.e_shoff = file32_to_cpu(ehdr32.e_shoff); +- ehdr.e_flags = file32_to_cpu(ehdr32.e_flags); +- ehdr.e_ehsize = file16_to_cpu(ehdr32.e_ehsize); +- ehdr.e_phentsize = file16_to_cpu(ehdr32.e_phentsize); +- ehdr.e_phnum = file16_to_cpu(ehdr32.e_phnum); +- ehdr.e_shentsize = file16_to_cpu(ehdr32.e_shentsize); +- ehdr.e_shnum = file16_to_cpu(ehdr32.e_shnum); +- ehdr.e_shstrndx = file16_to_cpu(ehdr32.e_shstrndx); +- +- if (ehdr.e_version != EV_CURRENT) { +- fprintf(stderr, "Bad Elf header version %u\n", +- ehdr.e_version); +- exit(11); +- } +- if (ehdr.e_phentsize != sizeof(Elf32_Phdr)) { +- fprintf(stderr, "Bad Elf progra header size %u expected %zu\n", +- ehdr.e_phentsize, sizeof(Elf32_Phdr)); +- exit(12); +- } +- phdrs32_size = ehdr.e_phnum * sizeof(Elf32_Phdr); +- phdr32 = calloc(ehdr.e_phnum, sizeof(Elf32_Phdr)); +- if (!phdr32) { +- fprintf(stderr, "Calloc of %u phdrs32 failed: %s\n", +- ehdr.e_phnum, strerror(errno)); +- exit(14); +- } +- phdr = calloc(ehdr.e_phnum, sizeof(Elf64_Phdr)); +- if (!phdr) { +- fprintf(stderr, "Calloc of %u phdrs failed: %s\n", +- ehdr.e_phnum, strerror(errno)); +- exit(15); +- } +- ret = pread(fd, phdr32, phdrs32_size, ehdr.e_phoff); +- if (ret < 0 || (size_t)ret != phdrs32_size) { +- fprintf(stderr, "Read of program header @ 0x%llu for %zu bytes failed: %s\n", +- (unsigned long long)ehdr.e_phoff, phdrs32_size, strerror(errno)); +- exit(16); +- } +- for (i = 0; i < ehdr.e_phnum; i++) { +- phdr[i].p_type = file32_to_cpu(phdr32[i].p_type); +- phdr[i].p_offset = file32_to_cpu(phdr32[i].p_offset); +- phdr[i].p_vaddr = file32_to_cpu(phdr32[i].p_vaddr); +- phdr[i].p_paddr = file32_to_cpu(phdr32[i].p_paddr); +- phdr[i].p_filesz = file32_to_cpu(phdr32[i].p_filesz); +- phdr[i].p_memsz = file32_to_cpu(phdr32[i].p_memsz); +- phdr[i].p_flags = file32_to_cpu(phdr32[i].p_flags); +- phdr[i].p_align = file32_to_cpu(phdr32[i].p_align); +- } +- free(phdr32); +-} +- +- +-static void read_elf64(int fd) +-{ +- Elf64_Ehdr ehdr64; +- Elf64_Phdr *phdr64; +- size_t phdrs_size; +- ssize_t ret, i; +- +- ret = pread(fd, &ehdr64, sizeof(ehdr64), 0); +- if (ret < 0 || (size_t)ret != sizeof(ehdr)) { +- fprintf(stderr, "Read of Elf header from %s failed: %s\n", +- fname, strerror(errno)); +- exit(10); +- } +- +- ehdr.e_type = file16_to_cpu(ehdr64.e_type); +- ehdr.e_machine = file16_to_cpu(ehdr64.e_machine); +- ehdr.e_version = file32_to_cpu(ehdr64.e_version); +- ehdr.e_entry = file64_to_cpu(ehdr64.e_entry); +- ehdr.e_phoff = file64_to_cpu(ehdr64.e_phoff); +- ehdr.e_shoff = file64_to_cpu(ehdr64.e_shoff); +- ehdr.e_flags = file32_to_cpu(ehdr64.e_flags); +- ehdr.e_ehsize = file16_to_cpu(ehdr64.e_ehsize); +- ehdr.e_phentsize = file16_to_cpu(ehdr64.e_phentsize); +- ehdr.e_phnum = file16_to_cpu(ehdr64.e_phnum); +- ehdr.e_shentsize = file16_to_cpu(ehdr64.e_shentsize); +- ehdr.e_shnum = file16_to_cpu(ehdr64.e_shnum); +- ehdr.e_shstrndx = file16_to_cpu(ehdr64.e_shstrndx); +- +- if (ehdr.e_version != EV_CURRENT) { +- fprintf(stderr, "Bad Elf header version %u\n", +- ehdr.e_version); +- exit(11); +- } +- if (ehdr.e_phentsize != sizeof(Elf64_Phdr)) { +- fprintf(stderr, "Bad Elf progra header size %u expected %zu\n", +- ehdr.e_phentsize, sizeof(Elf64_Phdr)); +- exit(12); +- } +- phdrs_size = ehdr.e_phnum * sizeof(Elf64_Phdr); +- phdr64 = calloc(ehdr.e_phnum, sizeof(Elf64_Phdr)); +- if (!phdr64) { +- fprintf(stderr, "Calloc of %u phdrs64 failed: %s\n", +- ehdr.e_phnum, strerror(errno)); +- exit(14); +- } +- phdr = calloc(ehdr.e_phnum, sizeof(Elf64_Phdr)); +- if (!phdr) { +- fprintf(stderr, "Calloc of %u phdrs failed: %s\n", +- ehdr.e_phnum, strerror(errno)); +- exit(15); +- } +- ret = pread(fd, phdr64, phdrs_size, ehdr.e_phoff); +- if (ret < 0 || (size_t)ret != phdrs_size) { +- fprintf(stderr, "Read of program header @ %llu for %zu bytes failed: %s\n", +- (unsigned long long)(ehdr.e_phoff), phdrs_size, strerror(errno)); +- exit(16); +- } +- for (i = 0; i < ehdr.e_phnum; i++) { +- phdr[i].p_type = file32_to_cpu(phdr64[i].p_type); +- phdr[i].p_flags = file32_to_cpu(phdr64[i].p_flags); +- phdr[i].p_offset = file64_to_cpu(phdr64[i].p_offset); +- phdr[i].p_vaddr = file64_to_cpu(phdr64[i].p_vaddr); +- phdr[i].p_paddr = file64_to_cpu(phdr64[i].p_paddr); +- phdr[i].p_filesz = file64_to_cpu(phdr64[i].p_filesz); +- phdr[i].p_memsz = file64_to_cpu(phdr64[i].p_memsz); +- phdr[i].p_align = file64_to_cpu(phdr64[i].p_align); +- } +- free(phdr64); +-} +- +-static void scan_vmcoreinfo(char *start, size_t size) +-{ +- char *last = start + size - 1; +- char *pos, *eol; +- char temp_buf[1024]; +- bool last_line = false; +- char *str; +- +-#define SYMBOL(sym) { \ +- .str = "SYMBOL(" #sym ")=", \ +- .name = #sym, \ +- .len = sizeof("SYMBOL(" #sym ")=") - 1, \ +- .vaddr = & sym ## _vaddr, \ +- } +- static struct symbol { +- const char *str; +- const char *name; +- size_t len; +- loff_t *vaddr; +- } symbol[] = { +- SYMBOL(log_buf), +- SYMBOL(log_end), +- SYMBOL(log_buf_len), +- SYMBOL(logged_chars), +- SYMBOL(log_first_idx), +- SYMBOL(log_next_idx), +- }; +- +- for (pos = start; pos <= last; pos = eol + 1) { +- size_t len, i; +- /* Find the end of the current line */ +- for (eol = pos; (eol <= last) && (*eol != '\n') ; eol++) +- ; +- if (eol > last) { +- /* +- * We did not find \n and note ended. Currently kernel +- * is appending last field CRASH_TIME without \n. It +- * is ugly but handle it. +- */ +- eol = last; +- len = eol - pos + 1; +- if (len >= sizeof(temp_buf)) +- len = sizeof(temp_buf) - 1; +- strncpy(temp_buf, pos, len); +- temp_buf[len + 1] = '\0'; +- +- pos = temp_buf; +- len = len + 1; +- eol = pos + len -1; +- last_line = true; +- } else { +- len = eol - pos + 1; +- } +- +- /* Stomp the last character so I am guaranteed a terminating null */ +- *eol = '\0'; +- /* Copy OSRELEASE if I see it */ +- if ((len >= 10) && (memcmp("OSRELEASE=", pos, 10) == 0)) { +- size_t to_copy = len - 10; +- if (to_copy >= sizeof(osrelease)) +- to_copy = sizeof(osrelease) - 1; +- memcpy(osrelease, pos + 10, to_copy); +- osrelease[to_copy] = '\0'; +- } +- /* See if the line is mentions a symbol I am looking for */ +- for (i = 0; i < sizeof(symbol)/sizeof(symbol[0]); i++ ) { +- unsigned long long vaddr; +- if (symbol[i].len >= len) +- continue; +- if (memcmp(symbol[i].str, pos, symbol[i].len) != 0) +- continue; +- /* Found a symbol now decode it */ +- vaddr = strtoull(pos + symbol[i].len, NULL, 16); +- /* Remember the virtual address */ +- *symbol[i].vaddr = vaddr; +- } +- +- /* Check for "SIZE(printk_log)" or older "SIZE(log)=" */ +- str = "SIZE(log)="; +- if (memcmp(str, pos, strlen(str)) == 0) +- log_sz = strtoull(pos + strlen(str), NULL, 10); +- +- str = "SIZE(printk_log)="; +- if (memcmp(str, pos, strlen(str)) == 0) +- log_sz = strtoull(pos + strlen(str), NULL, 10); +- +- /* Check for struct printk_log (or older log) field offsets */ +- str = "OFFSET(log.ts_nsec)="; +- if (memcmp(str, pos, strlen(str)) == 0) +- log_offset_ts_nsec = strtoull(pos + strlen(str), NULL, +- 10); +- str = "OFFSET(printk_log.ts_nsec)="; +- if (memcmp(str, pos, strlen(str)) == 0) +- log_offset_ts_nsec = strtoull(pos + strlen(str), NULL, +- 10); +- +- str = "OFFSET(log.len)="; +- if (memcmp(str, pos, strlen(str)) == 0) +- log_offset_len = strtoul(pos + strlen(str), NULL, 10); +- +- str = "OFFSET(printk_log.len)="; +- if (memcmp(str, pos, strlen(str)) == 0) +- log_offset_len = strtoul(pos + strlen(str), NULL, 10); +- +- str = "OFFSET(log.text_len)="; +- if (memcmp(str, pos, strlen(str)) == 0) +- log_offset_text_len = strtoul(pos + strlen(str), NULL, +- 10); +- str = "OFFSET(printk_log.text_len)="; +- if (memcmp(str, pos, strlen(str)) == 0) +- log_offset_text_len = strtoul(pos + strlen(str), NULL, +- 10); +- +- if (last_line) +- break; +- } +-} +- +-static void scan_notes(int fd, loff_t start, loff_t lsize) +-{ +- char *buf, *last, *note, *next; +- size_t size; +- ssize_t ret; +- +- if (lsize > SSIZE_MAX) { +- fprintf(stderr, "Unable to handle note section of %llu bytes\n", +- (unsigned long long)lsize); +- exit(20); +- } +- size = lsize; +- buf = malloc(size); +- if (!buf) { +- fprintf(stderr, "Cannot malloc %zu bytes\n", size); +- exit(21); +- } +- last = buf + size - 1; +- ret = pread(fd, buf, size, start); +- if (ret != (ssize_t)size) { +- fprintf(stderr, "Cannot read note section @ 0x%llx of %zu bytes: %s\n", +- (unsigned long long)start, size, strerror(errno)); +- exit(22); +- } +- +- for (note = buf; (note + sizeof(Elf_Nhdr)) < last; note = next) +- { +- Elf_Nhdr *hdr; +- char *n_name, *n_desc; +- size_t n_namesz, n_descsz, n_type; +- +- hdr = (Elf_Nhdr *)note; +- n_namesz = file32_to_cpu(hdr->n_namesz); +- n_descsz = file32_to_cpu(hdr->n_descsz); +- n_type = file32_to_cpu(hdr->n_type); +- +- n_name = note + sizeof(*hdr); +- n_desc = n_name + ((n_namesz + 3) & ~3); +- next = n_desc + ((n_descsz + 3) & ~3); +- +- if (next > (last + 1)) +- break; +- +- if ((memcmp(n_name, "VMCOREINFO", 11) != 0) || (n_type != 0)) +- continue; +- scan_vmcoreinfo(n_desc, n_descsz); +- } +- free(buf); +-} +- +-static void scan_note_headers(int fd) +-{ +- int i; +- for (i = 0; i < ehdr.e_phnum; i++) { +- if (phdr[i].p_type != PT_NOTE) +- continue; +- scan_notes(fd, phdr[i].p_offset, phdr[i].p_filesz); +- } +-} +- +-static uint64_t read_file_pointer(int fd, uint64_t addr) +-{ +- uint64_t result; +- ssize_t ret; +- +- if (machine_pointer_bits() == 64) { +- uint64_t scratch; +- ret = pread(fd, &scratch, sizeof(scratch), addr); +- if (ret != sizeof(scratch)) { +- fprintf(stderr, "Failed to read pointer @ 0x%llx: %s\n", +- (unsigned long long)addr, strerror(errno)); +- exit(40); +- } +- result = file64_to_cpu(scratch); +- } else { +- uint32_t scratch; +- ret = pread(fd, &scratch, sizeof(scratch), addr); +- if (ret != sizeof(scratch)) { +- fprintf(stderr, "Failed to read pointer @ 0x%llx: %s\n", +- (unsigned long long)addr, strerror(errno)); +- exit(40); +- } +- result = file32_to_cpu(scratch); +- } +- return result; +-} +- +-static uint32_t read_file_u32(int fd, uint64_t addr) +-{ +- uint32_t scratch; +- ssize_t ret; +- ret = pread(fd, &scratch, sizeof(scratch), addr); +- if (ret != sizeof(scratch)) { +- fprintf(stderr, "Failed to read value @ 0x%llx: %s\n", +- (unsigned long long)addr, strerror(errno)); +- exit(41); +- } +- return file32_to_cpu(scratch); +-} +- +-static int32_t read_file_s32(int fd, uint64_t addr) +-{ +- return read_file_u32(fd, addr); +-} +- +-static void write_to_stdout(char *buf, unsigned int nr) +-{ +- ssize_t ret; +- +- ret = write(STDOUT_FILENO, buf, nr); +- if (ret != nr) { +- fprintf(stderr, "Failed to write out the dmesg log buffer!:" +- " %s\n", strerror(errno)); +- exit(54); +- } +-} +- +-static void dump_dmesg_legacy(int fd) +-{ +- uint64_t log_buf, log_buf_offset; +- unsigned log_end, logged_chars, log_end_wrapped; +- int log_buf_len, to_wrap; +- char *buf; +- ssize_t ret; +- +- if (!log_buf_vaddr) { +- fprintf(stderr, "Missing the log_buf symbol\n"); +- exit(50); +- } +- if (!log_end_vaddr) { +- fprintf(stderr, "Missing the log_end symbol\n"); +- exit(51); +- } +- if (!log_buf_len_vaddr) { +- fprintf(stderr, "Missing the log_bug_len symbol\n"); +- exit(52); +- } +- if (!logged_chars_vaddr) { +- fprintf(stderr, "Missing the logged_chars symbol\n"); +- exit(53); +- } +- +- +- log_buf = read_file_pointer(fd, vaddr_to_offset(log_buf_vaddr)); +- log_end = read_file_u32(fd, vaddr_to_offset(log_end_vaddr)); +- log_buf_len = read_file_s32(fd, vaddr_to_offset(log_buf_len_vaddr)); +- logged_chars = read_file_u32(fd, vaddr_to_offset(logged_chars_vaddr)); +- +- log_buf_offset = vaddr_to_offset(log_buf); +- +- buf = calloc(1, log_buf_len); +- if (!buf) { +- fprintf(stderr, "Failed to malloc %d bytes for the logbuf: %s\n", +- log_buf_len, strerror(errno)); +- exit(51); +- } +- +- log_end_wrapped = log_end % log_buf_len; +- to_wrap = log_buf_len - log_end_wrapped; +- +- ret = pread(fd, buf, to_wrap, log_buf_offset + log_end_wrapped); +- if (ret != to_wrap) { +- fprintf(stderr, "Failed to read the first half of the log buffer: %s\n", +- strerror(errno)); +- exit(52); +- } +- ret = pread(fd, buf + to_wrap, log_end_wrapped, log_buf_offset); +- if (ret != log_end_wrapped) { +- fprintf(stderr, "Faield to read the second half of the log buffer: %s\n", +- strerror(errno)); +- exit(53); +- } +- +- /* +- * To collect full dmesg including the part before `dmesg -c` is useful +- * for later debugging. Use same logic as what crash utility is using. +- */ +- logged_chars = log_end < log_buf_len ? log_end : log_buf_len; +- +- write_to_stdout(buf + (log_buf_len - logged_chars), logged_chars); +-} +- +-static inline uint16_t struct_val_u16(char *ptr, unsigned int offset) +-{ +- return(file16_to_cpu(*(uint16_t *)(ptr + offset))); +-} +- +-static inline uint32_t struct_val_u32(char *ptr, unsigned int offset) +-{ +- return(file32_to_cpu(*(uint32_t *)(ptr + offset))); +-} +- +-static inline uint64_t struct_val_u64(char *ptr, unsigned int offset) +-{ +- return(file64_to_cpu(*(uint64_t *)(ptr + offset))); +-} +- +-/* Read headers of log records and dump accordingly */ +-static void dump_dmesg_structured(int fd) +-{ +-#define OUT_BUF_SIZE 4096 +- uint64_t log_buf, log_buf_offset, ts_nsec; +- uint32_t log_first_idx, log_next_idx, current_idx, len = 0, i; +- char *buf, out_buf[OUT_BUF_SIZE]; +- ssize_t ret; +- char *msg; +- uint16_t text_len; +- imaxdiv_t imaxdiv_sec, imaxdiv_usec; +- +- if (!log_buf_vaddr) { +- fprintf(stderr, "Missing the log_buf symbol\n"); +- exit(60); +- } +- +- if (!log_buf_len_vaddr) { +- fprintf(stderr, "Missing the log_bug_len symbol\n"); +- exit(61); +- } +- +- if (!log_first_idx_vaddr) { +- fprintf(stderr, "Missing the log_first_idx symbol\n"); +- exit(62); +- } +- +- if (!log_next_idx_vaddr) { +- fprintf(stderr, "Missing the log_next_idx symbol\n"); +- exit(63); +- } +- +- if (!log_sz) { +- fprintf(stderr, "Missing the struct log size export\n"); +- exit(64); +- } +- +- if (log_offset_ts_nsec == UINT64_MAX) { +- fprintf(stderr, "Missing the log.ts_nsec offset export\n"); +- exit(65); +- } +- +- if (log_offset_len == UINT16_MAX) { +- fprintf(stderr, "Missing the log.len offset export\n"); +- exit(66); +- } +- +- if (log_offset_text_len == UINT16_MAX) { +- fprintf(stderr, "Missing the log.text_len offset export\n"); +- exit(67); +- } +- +- log_buf = read_file_pointer(fd, vaddr_to_offset(log_buf_vaddr)); +- +- log_first_idx = read_file_u32(fd, vaddr_to_offset(log_first_idx_vaddr)); +- log_next_idx = read_file_u32(fd, vaddr_to_offset(log_next_idx_vaddr)); +- +- log_buf_offset = vaddr_to_offset(log_buf); +- +- buf = calloc(1, log_sz); +- if (!buf) { +- fprintf(stderr, "Failed to malloc %" PRId64 " bytes for the log:" +- " %s\n", log_sz, strerror(errno)); +- exit(64); +- } +- +- /* Parse records and write out data at standard output */ +- +- current_idx = log_first_idx; +- len = 0; +- while (current_idx != log_next_idx) { +- uint16_t loglen; +- +- ret = pread(fd, buf, log_sz, log_buf_offset + current_idx); +- if (ret != log_sz) { +- fprintf(stderr, "Failed to read log of size %" PRId64 " bytes:" +- " %s\n", log_sz, strerror(errno)); +- exit(65); +- } +- ts_nsec = struct_val_u64(buf, log_offset_ts_nsec); +- imaxdiv_sec = imaxdiv(ts_nsec, 1000000000); +- imaxdiv_usec = imaxdiv(imaxdiv_sec.rem, 1000); +- +- len += sprintf(out_buf + len, "[%5llu.%06llu] ", +- (long long unsigned int)imaxdiv_sec.quot, +- (long long unsigned int)imaxdiv_usec.quot); +- +- /* escape non-printable characters */ +- text_len = struct_val_u16(buf, log_offset_text_len); +- msg = calloc(1, text_len); +- if (!msg) { +- fprintf(stderr, "Failed to malloc %u bytes for log text:" +- " %s\n", text_len, strerror(errno)); +- exit(64); +- } +- +- ret = pread(fd, msg, text_len, log_buf_offset + current_idx + log_sz); +- if (ret != text_len) { +- fprintf(stderr, "Failed to read log text of size %u bytes:" +- " %s\n", text_len, strerror(errno)); +- exit(65); +- } +- for (i = 0; i < text_len; i++) { +- unsigned char c = msg[i]; +- +- if (!isprint(c) && !isspace(c)) +- len += sprintf(out_buf + len, "\\x%02x", c); +- else +- out_buf[len++] = c; +- +- if (len >= OUT_BUF_SIZE - 64) { +- write_to_stdout(out_buf, len); +- len = 0; +- } +- } +- +- out_buf[len++] = '\n'; +- free(msg); +- /* +- * A length == 0 record is the end of buffer marker. Wrap around +- * and read the message at the start of the buffer. +- */ +- loglen = struct_val_u16(buf, log_offset_len); +- if (!loglen) +- current_idx = 0; +- else +- /* Move to next record */ +- current_idx += loglen; +- } +- free(buf); +- if (len) +- write_to_stdout(out_buf, len); +-} +- +-static void dump_dmesg(int fd) +-{ +- if (log_first_idx_vaddr) +- dump_dmesg_structured(fd); +- else +- dump_dmesg_legacy(fd); +-} + + int main(int argc, char **argv) + { +@@ -724,42 +22,10 @@ int main(int argc, char **argv) + fname, strerror(errno)); + return 2; + } +- ret = pread(fd, ehdr.e_ident, EI_NIDENT, 0); +- if (ret != EI_NIDENT) { +- fprintf(stderr, "Read of e_ident from %s failed: %s\n", +- fname, strerror(errno)); +- return 3; +- } +- if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0) { +- fprintf(stderr, "Missing elf signature\n"); +- return 4; +- } +- if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) { +- fprintf(stderr, "Bad elf version\n"); +- return 5; +- } +- if ((ehdr.e_ident[EI_CLASS] != ELFCLASS32) && +- (ehdr.e_ident[EI_CLASS] != ELFCLASS64)) +- { +- fprintf(stderr, "Unknown elf class %u\n", +- ehdr.e_ident[EI_CLASS]); +- return 6; +- } +- if ((ehdr.e_ident[EI_DATA] != ELFDATA2LSB) && +- (ehdr.e_ident[EI_DATA] != ELFDATA2MSB)) +- { +- fprintf(stderr, "Unkown elf data order %u\n", +- ehdr.e_ident[EI_DATA]); +- return 7; +- } +- if (ehdr.e_ident[EI_CLASS] == ELFCLASS32) +- read_elf32(fd); +- else +- read_elf64(fd); + +- scan_note_headers(fd); +- dump_dmesg(fd); ++ ret = read_elf_vmcore(fd); ++ + close(fd); + +- return 0; ++ return ret; + } diff -Nru kexec-tools-2.0.18/debian/patches/lp2024479-0002-arm64-Add-support-to-read-PHYS_OFFSET-from-kcore-pt_.patch kexec-tools-2.0.18/debian/patches/lp2024479-0002-arm64-Add-support-to-read-PHYS_OFFSET-from-kcore-pt_.patch --- kexec-tools-2.0.18/debian/patches/lp2024479-0002-arm64-Add-support-to-read-PHYS_OFFSET-from-kcore-pt_.patch 1970-01-01 02:00:00.000000000 +0200 +++ kexec-tools-2.0.18/debian/patches/lp2024479-0002-arm64-Add-support-to-read-PHYS_OFFSET-from-kcore-pt_.patch 2023-07-03 16:29:58.000000000 +0300 @@ -0,0 +1,341 @@ +From 3ef9961af57936ef6956c08f6af0a4bbe50d5b8d Mon Sep 17 00:00:00 2001 +From: Bhupesh Sharma +Origin: upstream, https://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git/commit?id=3ef9961af57936ef6956c08f6af0a4bbe50d5b8d +Bug-Ubuntu: https://bugs.launchpad.net/bugs/2024479 +Date: Thu, 20 Dec 2018 16:52:52 +0530 +Subject: [PATCH 2/6] arm64: Add support to read PHYS_OFFSET from 'kcore' - + pt_note or pt_load (if available) + +On certain arm64 platforms, it has been noticed that due +to a hole at the start of physical ram exposed to kernel +(i.e. it doesn't start from address 0), the kernel still +calculates the 'memstart_addr' kernel variable as 0. + +Whereas the SYSTEM_RAM or IOMEM_RESERVED range in '/proc/iomem' +would carry a first entry whose start address is non-zero +(as the physical ram exposed to the kernel starts from a +non-zero address). + +In such cases, if we rely on '/proc/iomem' entries to +calculate the phys_offset, then we will have mismatch +between the user-space and kernel space 'PHYS_OFFSET' +value. The present 'kexec-tools' code does the same +in 'get_memory_ranges_iomem_cb()' function when it makes +a call to 'set_phys_offset()'. This can cause the vmcore +generated via 'kexec-tools' to miss the last few bytes as +the first '/proc/iomem' starts from a non-zero address. + +Please see [0] for the original bug-report from Yanjiang Jin. + +The same can be fixed in the following manner: + +1. For newer kernel (>= 4.19, with commit 23c85094fe1895caefdd +["proc/kcore: add vmcoreinfo note to /proc/kcore"] available), +'kcore' contains a new PT_NOTE which carries the VMCOREINFO +information. + +If the same is available, one should prefer the same to +retrieve 'PHYS_OFFSET' value exported by the kernel as this +is now the standard interface exposed by kernel for sharing +machine specific details with the user-land as per +the arm64 kernel maintainers (see [1]) . + +2. For older kernels, we can try and determine the PHYS_OFFSET +value from PT_LOAD segments inside 'kcore' via some jugglery +of the correct virtual and physical address combinations. + +As a fallback, we still support getting the PHYS_OFFSET values +from '/proc/iomem', to maintain backward compatibility. + +Testing: +------- +- Tested on my apm-mustang and qualcomm amberwing board with upstream + kernel (4.20.0-rc7) for both KASLR and non-KASLR boot cases. + +References: +----------- +[0] https://www.spinics.net/lists/kexec/msg20618.html +[1] https://www.mail-archive.com/kexec@lists.infradead.org/msg20300.html + +Reported-by: Yanjiang Jin +Signed-off-by: Bhupesh Sharma +Signed-off-by: Simon Horman +--- + kexec/arch/arm64/kexec-arm64.c | 194 ++++++++++++++++++++++++++++++++- + kexec/arch/arm64/kexec-arm64.h | 15 +-- + 2 files changed, 200 insertions(+), 9 deletions(-) + +--- a/kexec/arch/arm64/kexec-arm64.c ++++ b/kexec/arch/arm64/kexec-arm64.c +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -38,6 +39,21 @@ + #define PROP_ELFCOREHDR "linux,elfcorehdr" + #define PROP_USABLE_MEM_RANGE "linux,usable-memory-range" + ++#define PAGE_OFFSET_36 ((0xffffffffffffffffUL) << 36) ++#define PAGE_OFFSET_39 ((0xffffffffffffffffUL) << 39) ++#define PAGE_OFFSET_42 ((0xffffffffffffffffUL) << 42) ++#define PAGE_OFFSET_47 ((0xffffffffffffffffUL) << 47) ++#define PAGE_OFFSET_48 ((0xffffffffffffffffUL) << 48) ++ ++/* Global flag which indicates that we have tried reading ++ * PHYS_OFFSET from 'kcore' already. ++ */ ++static bool try_read_phys_offset_from_kcore = false; ++ ++/* Machine specific details. */ ++static int va_bits; ++static unsigned long page_offset; ++ + /* Global varables the core kexec routines expect. */ + + unsigned char reuse_initrd; +@@ -750,6 +766,126 @@ void add_segment(struct kexec_info *info + add_segment_phys_virt(info, buf, bufsz, base, memsz, 1); + } + ++static inline void set_phys_offset(uint64_t v, char *set_method) ++{ ++ if (arm64_mem.phys_offset == arm64_mem_ngv ++ || v < arm64_mem.phys_offset) { ++ arm64_mem.phys_offset = v; ++ dbgprintf("%s: phys_offset : %016lx (method : %s)\n", ++ __func__, arm64_mem.phys_offset, ++ set_method); ++ } ++} ++ ++/** ++ * get_va_bits - Helper for getting VA_BITS ++ */ ++ ++static int get_va_bits(void) ++{ ++ unsigned long long stext_sym_addr = get_kernel_sym("_stext"); ++ ++ if (stext_sym_addr == 0) { ++ fprintf(stderr, "Can't get the symbol of _stext.\n"); ++ return -1; ++ } ++ ++ /* Derive va_bits as per arch/arm64/Kconfig */ ++ if ((stext_sym_addr & PAGE_OFFSET_36) == PAGE_OFFSET_36) { ++ va_bits = 36; ++ } else if ((stext_sym_addr & PAGE_OFFSET_39) == PAGE_OFFSET_39) { ++ va_bits = 39; ++ } else if ((stext_sym_addr & PAGE_OFFSET_42) == PAGE_OFFSET_42) { ++ va_bits = 42; ++ } else if ((stext_sym_addr & PAGE_OFFSET_47) == PAGE_OFFSET_47) { ++ va_bits = 47; ++ } else if ((stext_sym_addr & PAGE_OFFSET_48) == PAGE_OFFSET_48) { ++ va_bits = 48; ++ } else { ++ fprintf(stderr, ++ "Cannot find a proper _stext for calculating VA_BITS\n"); ++ return -1; ++ } ++ ++ dbgprintf("va_bits : %d\n", va_bits); ++ ++ return 0; ++} ++ ++/** ++ * get_page_offset - Helper for getting PAGE_OFFSET ++ */ ++ ++static int get_page_offset(void) ++{ ++ int ret; ++ ++ ret = get_va_bits(); ++ if (ret < 0) ++ return ret; ++ ++ page_offset = (0xffffffffffffffffUL) << (va_bits - 1); ++ dbgprintf("page_offset : %lx\n", page_offset); ++ ++ return 0; ++} ++ ++/** ++ * get_phys_offset_from_vmcoreinfo_pt_note - Helper for getting PHYS_OFFSET ++ * from VMCOREINFO note inside 'kcore'. ++ */ ++ ++static int get_phys_offset_from_vmcoreinfo_pt_note(unsigned long *phys_offset) ++{ ++ int fd, ret = 0; ++ ++ if ((fd = open("/proc/kcore", O_RDONLY)) < 0) { ++ fprintf(stderr, "Can't open (%s).\n", "/proc/kcore"); ++ return EFAILED; ++ } ++ ++ ret = read_phys_offset_elf_kcore(fd, phys_offset); ++ ++ close(fd); ++ return ret; ++} ++ ++/** ++ * get_phys_base_from_pt_load - Helper for getting PHYS_OFFSET ++ * from PT_LOADs inside 'kcore'. ++ */ ++ ++int get_phys_base_from_pt_load(unsigned long *phys_offset) ++{ ++ int i, fd, ret; ++ unsigned long long phys_start; ++ unsigned long long virt_start; ++ ++ ret = get_page_offset(); ++ if (ret < 0) ++ return ret; ++ ++ if ((fd = open("/proc/kcore", O_RDONLY)) < 0) { ++ fprintf(stderr, "Can't open (%s).\n", "/proc/kcore"); ++ return EFAILED; ++ } ++ ++ read_elf_kcore(fd); ++ ++ for (i = 0; get_pt_load(i, ++ &phys_start, NULL, &virt_start, NULL); ++ i++) { ++ if (virt_start != NOT_KV_ADDR ++ && virt_start >= page_offset ++ && phys_start != NOT_PADDR) ++ *phys_offset = phys_start - ++ (virt_start & ~page_offset); ++ } ++ ++ close(fd); ++ return 0; ++} ++ + /** + * get_memory_ranges_iomem_cb - Helper for get_memory_ranges_iomem. + */ +@@ -757,11 +893,45 @@ void add_segment(struct kexec_info *info + static int get_memory_ranges_iomem_cb(void *data, int nr, char *str, + unsigned long long base, unsigned long long length) + { ++ int ret; ++ unsigned long phys_offset = UINT64_MAX; + struct memory_range *r; + + if (nr >= KEXEC_SEGMENT_MAX) + return -1; + ++ if (!try_read_phys_offset_from_kcore) { ++ /* Since kernel version 4.19, 'kcore' contains ++ * a new PT_NOTE which carries the VMCOREINFO ++ * information. ++ * If the same is available, one should prefer the ++ * same to retrieve 'PHYS_OFFSET' value exported by ++ * the kernel as this is now the standard interface ++ * exposed by kernel for sharing machine specific ++ * details with the userland. ++ */ ++ ret = get_phys_offset_from_vmcoreinfo_pt_note(&phys_offset); ++ if (!ret) { ++ if (phys_offset != UINT64_MAX) ++ set_phys_offset(phys_offset, ++ "vmcoreinfo pt_note"); ++ } else { ++ /* If we are running on a older kernel, ++ * try to retrieve the 'PHYS_OFFSET' value ++ * exported by the kernel in the 'kcore' ++ * file by reading the PT_LOADs and determining ++ * the correct combination. ++ */ ++ ret = get_phys_base_from_pt_load(&phys_offset); ++ if (!ret) ++ if (phys_offset != UINT64_MAX) ++ set_phys_offset(phys_offset, ++ "pt_load"); ++ } ++ ++ try_read_phys_offset_from_kcore = true; ++ } ++ + r = (struct memory_range *)data + nr; + + if (!strncmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM))) +@@ -774,7 +944,26 @@ static int get_memory_ranges_iomem_cb(vo + r->start = base; + r->end = base + length - 1; + +- set_phys_offset(r->start); ++ /* As a fallback option, we can try determining the PHYS_OFFSET ++ * value from the '/proc/iomem' entries as well. ++ * ++ * But note that this can be flaky, as on certain arm64 ++ * platforms, it has been noticed that due to a hole at the ++ * start of physical ram exposed to kernel ++ * (i.e. it doesn't start from address 0), the kernel still ++ * calculates the 'memstart_addr' kernel variable as 0. ++ * ++ * Whereas the SYSTEM_RAM or IOMEM_RESERVED range in ++ * '/proc/iomem' would carry a first entry whose start address ++ * is non-zero (as the physical ram exposed to the kernel ++ * starts from a non-zero address). ++ * ++ * In such cases, if we rely on '/proc/iomem' entries to ++ * calculate the phys_offset, then we will have mismatch ++ * 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); +@@ -783,7 +972,8 @@ static int get_memory_ranges_iomem_cb(vo + } + + /** +- * get_memory_ranges_iomem - Try to get the memory ranges from /proc/iomem. ++ * get_memory_ranges_iomem - Try to get the memory ranges from ++ * /proc/iomem. + */ + + static int get_memory_ranges_iomem(struct memory_range *array, +--- a/kexec/arch/arm64/kexec-arm64.h ++++ b/kexec/arch/arm64/kexec-arm64.h +@@ -21,6 +21,14 @@ + #define MiB(x) (KiB(x) * 1024UL) + #define GiB(x) (MiB(x) * 1024UL) + ++#define ULONGLONG_MAX (~0ULL) ++ ++/* ++ * Incorrect address ++ */ ++#define NOT_KV_ADDR (0x0) ++#define NOT_PADDR (ULONGLONG_MAX) ++ + int elf_arm64_probe(const char *kernel_buf, off_t kernel_size); + int elf_arm64_load(int argc, char **argv, const char *kernel_buf, + off_t kernel_size, struct kexec_info *info); +@@ -60,13 +68,6 @@ static inline void reset_vp_offset(void) + arm64_mem.vp_offset = arm64_mem_ngv; + } + +-static inline void set_phys_offset(uint64_t v) +-{ +- if (arm64_mem.phys_offset == arm64_mem_ngv +- || v < arm64_mem.phys_offset) +- arm64_mem.phys_offset = v; +-} +- + int arm64_process_image_header(const struct arm64_image_header *h); + unsigned long arm64_locate_kernel_segment(struct kexec_info *info); + int arm64_load_other_segments(struct kexec_info *info, diff -Nru kexec-tools-2.0.18/debian/patches/lp2024479-0003-arm64-kexec-allocate-memory-space-avoiding-reserved-.patch kexec-tools-2.0.18/debian/patches/lp2024479-0003-arm64-kexec-allocate-memory-space-avoiding-reserved-.patch --- kexec-tools-2.0.18/debian/patches/lp2024479-0003-arm64-kexec-allocate-memory-space-avoiding-reserved-.patch 1970-01-01 02:00:00.000000000 +0200 +++ kexec-tools-2.0.18/debian/patches/lp2024479-0003-arm64-kexec-allocate-memory-space-avoiding-reserved-.patch 2023-07-03 16:29:58.000000000 +0300 @@ -0,0 +1,245 @@ +From 342ffae1074731cf663a1daa27e8fe6423ba6421 Mon Sep 17 00:00:00 2001 +From: AKASHI Takahiro +Origin: upstream, https://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git/commit?id=342ffae1074731cf663a1daa27e8fe6423ba6421 +Bug-Ubuntu: https://bugs.launchpad.net/bugs/2024479 +Date: Wed, 18 Dec 2019 11:42:31 -0500 +Subject: [PATCH 3/6] 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. + +Signed-off-by: AKASHI Takahiro +Tested-by: Bhupesh Sharma +Tested-by: Masayoshi Mizuma +Signed-off-by: Simon Horman +--- + kexec/arch/arm64/kexec-arm64.c | 153 ++++++++++++++++++++------------- + 1 file changed, 94 insertions(+), 59 deletions(-) + +--- a/kexec/arch/arm64/kexec-arm64.c ++++ b/kexec/arch/arm64/kexec-arm64.c +@@ -10,7 +10,9 @@ + #include + #include + #include ++#include + #include ++#include + #include + #include + #include +@@ -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 +@@ -886,19 +889,33 @@ int get_phys_base_from_pt_load(unsigned + 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 +@@ -932,17 +949,72 @@ static int get_memory_ranges_iomem_cb(vo + 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. ++ */ ++ ++ /* 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); ++ } ++ } + +- r->start = base; +- r->end = base + length - 1; ++ 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. +@@ -963,52 +1035,15 @@ static int get_memory_ranges_iomem_cb(vo + * between the user-space and kernel space 'PHYS_OFFSET' + * value. + */ +- set_phys_offset(r->start, "iomem"); ++ if (memranges.size) ++ set_phys_offset(memranges.ranges[0].start, "iomem"); + +- dbgprintf("%s: %016llx - %016llx : %s", __func__, r->start, +- r->end, str); ++ dbgprint_mem_range("System RAM ranges;", ++ memranges.ranges, memranges.size); + + return 0; + } + +-/** +- * get_memory_ranges_iomem - Try to get the memory ranges from +- * /proc/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; +- } +- +- 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; diff -Nru kexec-tools-2.0.18/debian/patches/lp2024479-0004-kexec-add-variant-helper-functions-for-handling-memo.patch kexec-tools-2.0.18/debian/patches/lp2024479-0004-kexec-add-variant-helper-functions-for-handling-memo.patch --- kexec-tools-2.0.18/debian/patches/lp2024479-0004-kexec-add-variant-helper-functions-for-handling-memo.patch 1970-01-01 02:00:00.000000000 +0200 +++ kexec-tools-2.0.18/debian/patches/lp2024479-0004-kexec-add-variant-helper-functions-for-handling-memo.patch 2023-07-03 16:29:58.000000000 +0300 @@ -0,0 +1,84 @@ +From ea26c30fff12f8288194fdeb48fbd098272eea95 Mon Sep 17 00:00:00 2001 +From: AKASHI Takahiro +Origin: upstream, https://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git/commit?id=ea26c30fff12f8288194fdeb48fbd098272eea95 +Bug-Ubuntu: https://bugs.launchpad.net/bugs/2024479 +Date: Wed, 18 Dec 2019 11:42:30 -0500 +Subject: [PATCH 4/6] kexec: add variant helper functions for handling memory + regions + +mem_regions_alloc_and_add() and mem_regions_alloc_and_exclude() are +functionally equivalent to, respectively, mem_regions_add() and +mem_regions_exclude() except the formers will re-allocate memory +dynamically when no more entries are available in 'ranges' array. + +Signed-off-by: AKASHI Takahiro +Tested-by: Bhupesh Sharma +Tested-by: Masayoshi Mizuma +Signed-off-by: Simon Horman +--- + kexec/mem_regions.c | 42 ++++++++++++++++++++++++++++++++++++++++++ + kexec/mem_regions.h | 7 +++++++ + 2 files changed, 49 insertions(+) + +--- a/kexec/mem_regions.c ++++ b/kexec/mem_regions.c +@@ -125,3 +125,45 @@ int mem_regions_exclude(struct memory_ra + } + return 0; + } ++ ++#define KEXEC_MEMORY_RANGES 16 ++ ++int mem_regions_alloc_and_add(struct memory_ranges *ranges, ++ unsigned long long base, ++ unsigned long long length, int type) ++{ ++ void *new_ranges; ++ ++ if (ranges->size >= ranges->max_size) { ++ new_ranges = realloc(ranges->ranges, ++ sizeof(struct memory_range) * ++ (ranges->max_size + KEXEC_MEMORY_RANGES)); ++ if (!new_ranges) ++ return -1; ++ ++ ranges->ranges = new_ranges; ++ ranges->max_size += KEXEC_MEMORY_RANGES; ++ } ++ ++ return mem_regions_add(ranges, base, length, type); ++} ++ ++int mem_regions_alloc_and_exclude(struct memory_ranges *ranges, ++ const struct memory_range *range) ++{ ++ void *new_ranges; ++ ++ /* for safety, we should have at least one free entry in ranges */ ++ if (ranges->size >= ranges->max_size) { ++ new_ranges = realloc(ranges->ranges, ++ sizeof(struct memory_range) * ++ (ranges->max_size + KEXEC_MEMORY_RANGES)); ++ if (!new_ranges) ++ return -1; ++ ++ ranges->ranges = new_ranges; ++ ranges->max_size += KEXEC_MEMORY_RANGES; ++ } ++ ++ return mem_regions_exclude(ranges, range); ++} +--- a/kexec/mem_regions.h ++++ b/kexec/mem_regions.h +@@ -12,4 +12,11 @@ int mem_regions_exclude(struct memory_ra + int mem_regions_add(struct memory_ranges *ranges, unsigned long long base, + unsigned long long length, int type); + ++int mem_regions_alloc_and_exclude(struct memory_ranges *ranges, ++ const struct memory_range *range); ++ ++int mem_regions_alloc_and_add(struct memory_ranges *ranges, ++ unsigned long long base, ++ unsigned long long length, int type); ++ + #endif diff -Nru kexec-tools-2.0.18/debian/patches/lp2024479-0005-arm64-kdump-deal-with-a-lot-of-resource-entries-in-p.patch kexec-tools-2.0.18/debian/patches/lp2024479-0005-arm64-kdump-deal-with-a-lot-of-resource-entries-in-p.patch --- kexec-tools-2.0.18/debian/patches/lp2024479-0005-arm64-kdump-deal-with-a-lot-of-resource-entries-in-p.patch 1970-01-01 02:00:00.000000000 +0200 +++ kexec-tools-2.0.18/debian/patches/lp2024479-0005-arm64-kdump-deal-with-a-lot-of-resource-entries-in-p.patch 2023-07-03 16:29:58.000000000 +0300 @@ -0,0 +1,79 @@ +From 9ad927ad8eaff1b2ef9f948a7ff11f7f4fe9232e Mon Sep 17 00:00:00 2001 +From: AKASHI Takahiro +Origin: upstream, https://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git/commit?id=9ad927ad8eaff1b2ef9f948a7ff11f7f4fe9232e +Bug-Ubuntu: https://bugs.launchpad.net/bugs/2024479 +Date: Wed, 18 Dec 2019 11:42:32 -0500 +Subject: [PATCH 5/6] arm64: kdump: deal with a lot of resource entries in + /proc/iomem + +As described in the commit ("arm64: kexec: allocate memory space avoiding +reserved regions"), /proc/iomem now has a lot of "reserved" entries, and +it's not just enough to have a fixed size of memory range array. + +With this patch, kdump is allowed to handle arbitrary number of memory +ranges, using mem_regions_alloc_and_xxx() functions. + +Signed-off-by: AKASHI Takahiro +Tested-by: Bhupesh Sharma +Tested-by: Masayoshi Mizuma +Signed-off-by: Simon Horman +--- + kexec/arch/arm64/crashdump-arm64.c | 25 ++++++++++--------------- + 1 file changed, 10 insertions(+), 15 deletions(-) + +--- a/kexec/arch/arm64/crashdump-arm64.c ++++ b/kexec/arch/arm64/crashdump-arm64.c +@@ -23,13 +23,8 @@ + #include "kexec-elf.h" + #include "mem_regions.h" + +-/* memory ranges on crashed kernel */ +-static struct memory_range system_memory_ranges[CRASH_MAX_MEMORY_RANGES]; +-static struct memory_ranges system_memory_rgns = { +- .size = 0, +- .max_size = CRASH_MAX_MEMORY_RANGES, +- .ranges = system_memory_ranges, +-}; ++/* memory ranges of crashed kernel */ ++static struct memory_ranges system_memory_rgns; + + /* memory range reserved for crashkernel */ + struct memory_range crash_reserved_mem; +@@ -82,7 +77,7 @@ static uint64_t get_kernel_page_offset(v + * + * This function is called once for each memory region found in /proc/iomem. + * It locates system RAM and crashkernel reserved memory and places these to +- * variables, respectively, system_memory_ranges and crash_reserved_mem. ++ * variables, respectively, system_memory_rgns and usablemem_rgns. + */ + + static int iomem_range_callback(void *UNUSED(data), int UNUSED(nr), +@@ -90,11 +85,11 @@ static int iomem_range_callback(void *UN + unsigned long long length) + { + if (strncmp(str, CRASH_KERNEL, strlen(CRASH_KERNEL)) == 0) +- return mem_regions_add(&usablemem_rgns, +- base, length, RANGE_RAM); ++ return mem_regions_alloc_and_add(&usablemem_rgns, ++ base, length, RANGE_RAM); + else if (strncmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM)) == 0) +- return mem_regions_add(&system_memory_rgns, +- base, length, RANGE_RAM); ++ return mem_regions_alloc_and_add(&system_memory_rgns, ++ base, length, RANGE_RAM); + else if (strncmp(str, KERNEL_CODE, strlen(KERNEL_CODE)) == 0) + elf_info.kern_paddr_start = base; + else if (strncmp(str, KERNEL_DATA, strlen(KERNEL_DATA)) == 0) +@@ -135,9 +130,9 @@ static int crash_get_memory_ranges(void) + + dbgprint_mem_range("Reserved memory range", &crash_reserved_mem, 1); + +- if (mem_regions_exclude(&system_memory_rgns, &crash_reserved_mem)) { +- fprintf(stderr, +- "Error: Number of crash memory ranges excedeed the max limit\n"); ++ if (mem_regions_alloc_and_exclude(&system_memory_rgns, ++ &crash_reserved_mem)) { ++ fprintf(stderr, "Cannot allocate memory for ranges\n"); + return -ENOMEM; + } + diff -Nru kexec-tools-2.0.18/debian/patches/lp2024479-0006-arm64-support-more-than-one-crash-kernel-regions.patch kexec-tools-2.0.18/debian/patches/lp2024479-0006-arm64-support-more-than-one-crash-kernel-regions.patch --- kexec-tools-2.0.18/debian/patches/lp2024479-0006-arm64-support-more-than-one-crash-kernel-regions.patch 1970-01-01 02:00:00.000000000 +0200 +++ kexec-tools-2.0.18/debian/patches/lp2024479-0006-arm64-support-more-than-one-crash-kernel-regions.patch 2023-07-03 16:29:58.000000000 +0300 @@ -0,0 +1,330 @@ +From 0cf731b0c2fbd483c619f738441f8cc4fad663b2 Mon Sep 17 00:00:00 2001 +From: Chen Zhou +Origin: upstream, https://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git/commit?id=0cf731b0c2fbd483c619f738441f8cc4fad663b2 +Bug-Ubuntu: https://bugs.launchpad.net/bugs/2024479 +Date: Mon, 10 Jan 2022 18:20:08 +0800 +Subject: [PATCH 6/6] arm64: support more than one crash kernel regions + +When crashkernel is reserved above 4G in memory, kernel should +reserve some amount of low memory for swiotlb and some DMA buffers. +So there may be two crash kernel regions, one is below 4G, the other +is above 4G. + +Currently, there is only one crash kernel region on arm64, and pass +"linux,usable-memory-range = " property to crash dump +kernel. +Now, we pass "linux,usable-memory-range = " +to crash dump kernel to support two crash kernel regions and load crash +kernel high. Make the low memory region as the second range "BASE2 SIZE2" +to keep compatibility with existing user-space and older kdump kernels. + +Signed-off-by: Chen Zhou +Co-developed-by: Zhen Lei +Signed-off-by: Zhen Lei +Signed-off-by: Simon Horman +--- + kexec/arch/arm64/crashdump-arm64.c | 44 +++++++++------- + kexec/arch/arm64/crashdump-arm64.h | 5 +- + kexec/arch/arm64/kexec-arm64.c | 84 ++++++++++++++++++++---------- + 3 files changed, 86 insertions(+), 47 deletions(-) + +--- a/kexec/arch/arm64/crashdump-arm64.c ++++ b/kexec/arch/arm64/crashdump-arm64.c +@@ -27,11 +27,11 @@ + static struct memory_ranges system_memory_rgns; + + /* memory range reserved for crashkernel */ +-struct memory_range crash_reserved_mem; ++struct memory_range crash_reserved_mem[CRASH_MAX_RESERVED_RANGES]; + struct memory_ranges usablemem_rgns = { + .size = 0, +- .max_size = 1, +- .ranges = &crash_reserved_mem, ++ .max_size = CRASH_MAX_RESERVED_RANGES, ++ .ranges = crash_reserved_mem, + }; + + struct memory_range elfcorehdr_mem; +@@ -103,7 +103,7 @@ int is_crashkernel_mem_reserved(void) + if (!usablemem_rgns.size) + kexec_iomem_for_each_line(NULL, iomem_range_callback, NULL); + +- return crash_reserved_mem.start != crash_reserved_mem.end; ++ return usablemem_rgns.size; + } + + /* +@@ -117,6 +117,8 @@ int is_crashkernel_mem_reserved(void) + */ + static int crash_get_memory_ranges(void) + { ++ int i; ++ + /* + * First read all memory regions that can be considered as + * system memory including the crash area. +@@ -124,16 +126,19 @@ static int crash_get_memory_ranges(void) + if (!usablemem_rgns.size) + kexec_iomem_for_each_line(NULL, iomem_range_callback, NULL); + +- /* allow only a single region for crash dump kernel */ +- if (usablemem_rgns.size != 1) ++ /* allow one or two regions for crash dump kernel */ ++ if (!usablemem_rgns.size) + return -EINVAL; + +- dbgprint_mem_range("Reserved memory range", &crash_reserved_mem, 1); ++ dbgprint_mem_range("Reserved memory range", ++ usablemem_rgns.ranges, usablemem_rgns.size); + +- if (mem_regions_alloc_and_exclude(&system_memory_rgns, +- &crash_reserved_mem)) { +- fprintf(stderr, "Cannot allocate memory for ranges\n"); +- return -ENOMEM; ++ for (i = 0; i < usablemem_rgns.size; i++) { ++ if (mem_regions_alloc_and_exclude(&system_memory_rgns, ++ &crash_reserved_mem[i])) { ++ fprintf(stderr, "Cannot allocate memory for ranges\n"); ++ return -ENOMEM; ++ } + } + + /* +@@ -194,7 +199,8 @@ int load_crashdump_segments(struct kexec + return EFAILED; + + elfcorehdr = add_buffer_phys_virt(info, buf, bufsz, bufsz, 0, +- crash_reserved_mem.start, crash_reserved_mem.end, ++ crash_reserved_mem[usablemem_rgns.size - 1].start, ++ crash_reserved_mem[usablemem_rgns.size - 1].end, + -1, 0); + + elfcorehdr_mem.start = elfcorehdr; +@@ -212,21 +218,23 @@ int load_crashdump_segments(struct kexec + * virt_to_phys() in add_segment(). + * So let's fix up those values for later use so the memory base + * (arm64_mm.phys_offset) will be correctly replaced with +- * crash_reserved_mem.start. ++ * crash_reserved_mem[usablemem_rgns.size - 1].start. + */ + void fixup_elf_addrs(struct mem_ehdr *ehdr) + { + struct mem_phdr *phdr; + int i; + +- ehdr->e_entry += - arm64_mem.phys_offset + crash_reserved_mem.start; ++ ehdr->e_entry += -arm64_mem.phys_offset + ++ crash_reserved_mem[usablemem_rgns.size - 1].start; + + for (i = 0; i < ehdr->e_phnum; i++) { + phdr = &ehdr->e_phdr[i]; + if (phdr->p_type != PT_LOAD) + continue; + phdr->p_paddr += +- (-arm64_mem.phys_offset + crash_reserved_mem.start); ++ (-arm64_mem.phys_offset + ++ crash_reserved_mem[usablemem_rgns.size - 1].start); + } + } + +@@ -235,11 +243,11 @@ int get_crash_kernel_load_range(uint64_t + if (!usablemem_rgns.size) + kexec_iomem_for_each_line(NULL, iomem_range_callback, NULL); + +- if (!crash_reserved_mem.end) ++ if (!usablemem_rgns.size) + return -1; + +- *start = crash_reserved_mem.start; +- *end = crash_reserved_mem.end; ++ *start = crash_reserved_mem[usablemem_rgns.size - 1].start; ++ *end = crash_reserved_mem[usablemem_rgns.size - 1].end; + + return 0; + } +--- a/kexec/arch/arm64/crashdump-arm64.h ++++ b/kexec/arch/arm64/crashdump-arm64.h +@@ -16,8 +16,11 @@ + + #define CRASH_MAX_MEMORY_RANGES 32 + ++/* crash dump kernel support at most two regions, low_region and high region. */ ++#define CRASH_MAX_RESERVED_RANGES 2 ++ + extern struct memory_ranges usablemem_rgns; +-extern struct memory_range crash_reserved_mem; ++extern struct memory_range crash_reserved_mem[]; + extern struct memory_range elfcorehdr_mem; + + extern int load_crashdump_segments(struct kexec_info *info); +--- a/kexec/arch/arm64/kexec-arm64.c ++++ b/kexec/arch/arm64/kexec-arm64.c +@@ -385,22 +385,32 @@ static void fill_property(void *buf, uin + } + } + +-static int fdt_setprop_range(void *fdt, int nodeoffset, +- const char *name, struct memory_range *range, ++static int fdt_setprop_ranges(void *fdt, int nodeoffset, const char *name, ++ struct memory_range *ranges, int nr_ranges, bool reverse, + uint32_t address_cells, uint32_t size_cells) + { + void *buf, *prop; + size_t buf_size; +- int result; ++ int i, result; ++ struct memory_range *range; + +- buf_size = (address_cells + size_cells) * sizeof(uint32_t); ++ buf_size = (address_cells + size_cells) * sizeof(uint32_t) * nr_ranges; + prop = buf = xmalloc(buf_size); ++ if (!buf) ++ return -ENOMEM; ++ ++ for (i = 0; i < nr_ranges; i++) { ++ if (reverse) ++ range = ranges + (nr_ranges - 1 - i); ++ else ++ range = ranges + i; + +- fill_property(prop, range->start, address_cells); +- prop += address_cells * sizeof(uint32_t); ++ fill_property(prop, range->start, address_cells); ++ prop += address_cells * sizeof(uint32_t); + +- fill_property(prop, range->end - range->start + 1, size_cells); +- prop += size_cells * sizeof(uint32_t); ++ fill_property(prop, range->end - range->start + 1, size_cells); ++ prop += size_cells * sizeof(uint32_t); ++ } + + result = fdt_setprop(fdt, nodeoffset, name, buf, buf_size); + +@@ -422,7 +432,7 @@ static int setup_2nd_dtb(struct dtb *dtb + int len, range_len; + int nodeoffset; + int new_size; +- int result, kaslr_seed; ++ int i, result, kaslr_seed; + + result = fdt_check_header(dtb->buf); + +@@ -448,18 +458,20 @@ static int setup_2nd_dtb(struct dtb *dtb + goto on_error; + } + +- if (!cells_size_fitted(address_cells, size_cells, +- &crash_reserved_mem)) { +- fprintf(stderr, "kexec: usable memory range doesn't fit cells-size.\n"); +- result = -EINVAL; +- goto on_error; ++ for (i = 0; i < usablemem_rgns.size; i++) { ++ if (!cells_size_fitted(address_cells, size_cells, ++ &crash_reserved_mem[i])) { ++ fprintf(stderr, "kexec: usable memory range doesn't fit cells-size.\n"); ++ result = -EINVAL; ++ goto on_error; ++ } + } + + /* duplicate dt blob */ + range_len = sizeof(uint32_t) * (address_cells + size_cells); + new_size = fdt_totalsize(dtb->buf) + + fdt_prop_len(PROP_ELFCOREHDR, range_len) +- + fdt_prop_len(PROP_USABLE_MEM_RANGE, range_len); ++ + fdt_prop_len(PROP_USABLE_MEM_RANGE, range_len * usablemem_rgns.size); + + new_buf = xmalloc(new_size); + result = fdt_open_into(dtb->buf, new_buf, new_size); +@@ -543,8 +555,8 @@ static int setup_2nd_dtb(struct dtb *dtb + if (on_crash) { + /* add linux,elfcorehdr */ + nodeoffset = fdt_path_offset(new_buf, "/chosen"); +- result = fdt_setprop_range(new_buf, nodeoffset, +- PROP_ELFCOREHDR, &elfcorehdr_mem, ++ result = fdt_setprop_ranges(new_buf, nodeoffset, ++ PROP_ELFCOREHDR, &elfcorehdr_mem, 1, false, + address_cells, size_cells); + if (result) { + dbgprintf("%s: fdt_setprop failed: %s\n", __func__, +@@ -553,10 +565,17 @@ static int setup_2nd_dtb(struct dtb *dtb + goto on_error; + } + +- /* add linux,usable-memory-range */ ++ /* ++ * add linux,usable-memory-range ++ * ++ * crash dump kernel support one or two regions, to make ++ * compatibility with existing user-space and older kdump, the ++ * low region is always the last one. ++ */ + nodeoffset = fdt_path_offset(new_buf, "/chosen"); +- result = fdt_setprop_range(new_buf, nodeoffset, +- PROP_USABLE_MEM_RANGE, &crash_reserved_mem, ++ result = fdt_setprop_ranges(new_buf, nodeoffset, ++ PROP_USABLE_MEM_RANGE, ++ usablemem_rgns.ranges, usablemem_rgns.size, true, + address_cells, size_cells); + if (result) { + dbgprintf("%s: fdt_setprop failed: %s\n", __func__, +@@ -589,13 +608,13 @@ unsigned long arm64_locate_kernel_segmen + if (info->kexec_flags & KEXEC_ON_CRASH) { + unsigned long hole_end; + +- hole = (crash_reserved_mem.start < mem_min ? +- mem_min : crash_reserved_mem.start); ++ hole = (crash_reserved_mem[usablemem_rgns.size - 1].start < mem_min ? ++ mem_min : crash_reserved_mem[usablemem_rgns.size - 1].start); + hole = _ALIGN_UP(hole, MiB(2)); + hole_end = hole + arm64_mem.text_offset + arm64_mem.image_size; + + if ((hole_end > mem_max) || +- (hole_end > crash_reserved_mem.end)) { ++ (hole_end > crash_reserved_mem[usablemem_rgns.size - 1].end)) { + dbgprintf("%s: Crash kernel out of range\n", __func__); + hole = ULONG_MAX; + } +@@ -663,7 +682,7 @@ int arm64_load_other_segments(struct kex + + hole_min = image_base + arm64_mem.image_size; + if (info->kexec_flags & KEXEC_ON_CRASH) +- hole_max = crash_reserved_mem.end; ++ hole_max = crash_reserved_mem[usablemem_rgns.size - 1].end; + else + hole_max = ULONG_MAX; + +@@ -889,12 +908,21 @@ int get_phys_base_from_pt_load(unsigned + return 0; + } + +-static bool to_be_excluded(char *str) ++static bool to_be_excluded(char *str, unsigned long long start, unsigned long long end) + { ++ if (!strncmp(str, CRASH_KERNEL, strlen(CRASH_KERNEL))) { ++ uint64_t load_start, load_end; ++ ++ if (!get_crash_kernel_load_range(&load_start, &load_end) && ++ (load_start == start) && (load_end == end)) ++ return false; ++ ++ return true; ++ } ++ + 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))) ++ !strncmp(str, KERNEL_DATA, strlen(KERNEL_DATA))) + return false; + else + return true; +@@ -976,7 +1004,7 @@ int get_memory_ranges(struct memory_rang + memranges.size - 1, + memranges.ranges[memranges.size - 1].start, + memranges.ranges[memranges.size - 1].end); +- } else if (to_be_excluded(str)) { ++ } else if (to_be_excluded(str, start, end)) { + if (!memranges.size) + continue; + diff -Nru kexec-tools-2.0.18/debian/patches/series kexec-tools-2.0.18/debian/patches/series --- kexec-tools-2.0.18/debian/patches/series 2019-01-22 23:46:13.000000000 +0200 +++ kexec-tools-2.0.18/debian/patches/series 2023-07-03 16:29:58.000000000 +0300 @@ -5,3 +5,9 @@ coldreboot.patch linker-option.patch powerpcspe_support.patch +lp2024479-0001-util_lib-Add-functionality-to-read-elf-notes.patch +lp2024479-0002-arm64-Add-support-to-read-PHYS_OFFSET-from-kcore-pt_.patch +lp2024479-0003-arm64-kexec-allocate-memory-space-avoiding-reserved-.patch +lp2024479-0004-kexec-add-variant-helper-functions-for-handling-memo.patch +lp2024479-0005-arm64-kdump-deal-with-a-lot-of-resource-entries-in-p.patch +lp2024479-0006-arm64-support-more-than-one-crash-kernel-regions.patch