summaryrefslogtreecommitdiff
path: root/kexec/arch/i386/crashdump-x86.c
diff options
context:
space:
mode:
Diffstat (limited to 'kexec/arch/i386/crashdump-x86.c')
-rw-r--r--kexec/arch/i386/crashdump-x86.c63
1 files changed, 61 insertions, 2 deletions
diff --git a/kexec/arch/i386/crashdump-x86.c b/kexec/arch/i386/crashdump-x86.c
index 979c2bd..91872ab 100644
--- a/kexec/arch/i386/crashdump-x86.c
+++ b/kexec/arch/i386/crashdump-x86.c
@@ -100,6 +100,36 @@ static int get_kernel_paddr(struct kexec_info *UNUSED(info),
return -1;
}
+/* Retrieve kernel _stext symbol virtual address from /proc/kallsyms */
+static unsigned long long get_kernel_stext_sym(void)
+{
+ const char *kallsyms = "/proc/kallsyms";
+ const char *stext = "_stext";
+ char sym[128];
+ char line[128];
+ FILE *fp;
+ unsigned long long vaddr;
+ char type;
+
+ fp = fopen(kallsyms, "r");
+ if (!fp) {
+ fprintf(stderr, "Cannot open %s\n", kallsyms);
+ return 0;
+ }
+
+ while(fgets(line, sizeof(line), fp) != NULL) {
+ if (sscanf(line, "%Lx %c %s", &vaddr, &type, sym) != 3)
+ continue;
+ if (strcmp(sym, stext) == 0) {
+ dbgprintf("kernel symbol %s vaddr = %16llx\n", stext, vaddr);
+ return vaddr;
+ }
+ }
+
+ fprintf(stderr, "Cannot get kernel %s symbol address\n", stext);
+ return 0;
+}
+
/* Retrieve info regarding virtual address kernel has been compiled for and
* size of the kernel from /proc/kcore. Current /proc/kcore parsing from
* from kexec-tools fails because of malformed elf notes. A kernel patch has
@@ -118,6 +148,7 @@ static int get_kernel_vaddr_and_size(struct kexec_info *UNUSED(info),
int align;
off_t size;
uint32_t elf_flags = 0;
+ uint64_t stext_sym;
if (elf_info->machine != EM_X86_64)
return 0;
@@ -145,9 +176,36 @@ static int get_kernel_vaddr_and_size(struct kexec_info *UNUSED(info),
return -1;
}
- /* Traverse through the Elf headers and find the region where
- * kernel is mapped. */
end_phdr = &ehdr.e_phdr[ehdr.e_phnum];
+
+ /* Traverse through the Elf headers and find the region where
+ * _stext symbol is located in. That's where kernel is mapped */
+ stext_sym = get_kernel_stext_sym();
+ for(phdr = ehdr.e_phdr; stext_sym && phdr != end_phdr; phdr++) {
+ if (phdr->p_type == PT_LOAD) {
+ unsigned long long saddr = phdr->p_vaddr;
+ unsigned long long eaddr = phdr->p_vaddr + phdr->p_memsz;
+ unsigned long long size;
+
+ /* Look for kernel text mapping header. */
+ if (saddr < stext_sym && eaddr > stext_sym) {
+ saddr = _ALIGN_DOWN(saddr, X86_64_KERN_VADDR_ALIGN);
+ elf_info->kern_vaddr_start = saddr;
+ size = eaddr - saddr;
+ /* Align size to page size boundary. */
+ size = _ALIGN(size, align);
+ elf_info->kern_size = size;
+ dbgprintf("kernel vaddr = 0x%llx size = 0x%llx\n",
+ saddr, size);
+ return 0;
+ }
+ }
+ }
+
+ /* If failed to retrieve kernel text mapping through
+ * /proc/kallsyms, Traverse through the Elf headers again and
+ * find the region where kernel is mapped using hard-coded
+ * kernel mapping boundries */
for(phdr = ehdr.e_phdr; phdr != end_phdr; phdr++) {
if (phdr->p_type == PT_LOAD) {
unsigned long long saddr = phdr->p_vaddr;
@@ -169,6 +227,7 @@ static int get_kernel_vaddr_and_size(struct kexec_info *UNUSED(info),
}
}
}
+
fprintf(stderr, "Can't find kernel text map area from kcore\n");
return -1;
}