Those functions are going to be used for netdumps too.
---
Makefile | 7 +-
defs.h | 8 +
kaslr_helper.c | 463 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
sadump.c | 487 +++++----------------------------------------------------
symbols.c | 6 +-
5 files changed, 518 insertions(+), 453 deletions(-)
create mode 100644 kaslr_helper.c
diff --git a/Makefile b/Makefile
index bdc8321..f8647b3 100644
--- a/Makefile
+++ b/Makefile
@@ -71,7 +71,7 @@ CFILES=main.c tools.c global_data.c memory.c filesys.c help.c task.c \
xen_hyper.c xen_hyper_command.c xen_hyper_global_data.c \
xen_hyper_dump_tables.c kvmdump.c qemu.c qemu-load.c sadump.c ipcs.c \
ramdump.c vmware_vmss.c \
- xen_dom0.c
+ xen_dom0.c kaslr_helper.c
SOURCE_FILES=${CFILES} ${GENERIC_HFILES} ${MCORE_HFILES} \
${REDHAT_CFILES} ${REDHAT_HFILES} ${UNWIND_HFILES} \
@@ -90,7 +90,7 @@ OBJECT_FILES=main.o tools.o global_data.o memory.o filesys.o help.o
task.o \
xen_hyper.o xen_hyper_command.o xen_hyper_global_data.o \
xen_hyper_dump_tables.o kvmdump.o qemu.o qemu-load.o sadump.o ipcs.o \
ramdump.o vmware_vmss.o \
- xen_dom0.o
+ xen_dom0.o kaslr_helper.o
MEMORY_DRIVER_FILES=memory_driver/Makefile memory_driver/crash.c memory_driver/README
@@ -517,6 +517,9 @@ ramdump.o: ${GENERIC_HFILES} ${REDHAT_HFILES} ramdump.c
vmware_vmss.o: ${GENERIC_HFILES} ${VMWARE_HFILES} vmware_vmss.c
${CC} -c ${CRASH_CFLAGS} vmware_vmss.c ${WARNING_OPTIONS} ${WARNING_ERROR}
+kaslr_helper.o: ${GENERIC_HFILES} kaslr_helper.c
+ ${CC} -c ${CRASH_CFLAGS} kaslr_helper.c ${WARNING_OPTIONS} ${WARNING_ERROR}
+
${PROGRAM}: force
@make --no-print-directory all
diff --git a/defs.h b/defs.h
index 7998ebf..18b41d0 100644
--- a/defs.h
+++ b/defs.h
@@ -6334,6 +6334,7 @@ FILE *set_sadump_fp(FILE *);
void get_sadump_regs(struct bt_info *bt, ulong *ipp, ulong *spp);
void sadump_display_regs(int, FILE *);
int sadump_phys_base(ulong *);
+int sadump_set_phys_base(ulong);
void sadump_show_diskset(void);
int sadump_is_zero_excluded(void);
void sadump_set_zero_excluded(void);
@@ -6341,6 +6342,8 @@ void sadump_unset_zero_excluded(void);
struct sadump_data;
struct sadump_data *get_sadump_data(void);
int sadump_calc_kaslr_offset(ulong *);
+ulong sadump_get_cr3(void);
+ulong sadump_get_idtr(void);
/*
* qemu.c
@@ -6389,6 +6392,11 @@ uint vmware_vmss_page_size(void);
int read_vmware_vmss(int, void *, int, ulong, physaddr_t);
int write_vmware_vmss(int, void *, int, ulong, physaddr_t);
+/*
+ * kaslr_helper.c
+ */
+int calc_kaslr_offset(ulong *, ulong *);
+
/*
* gnu_binutils.c
*/
diff --git a/kaslr_helper.c b/kaslr_helper.c
new file mode 100644
index 0000000..e2da81c
--- /dev/null
+++ b/kaslr_helper.c
@@ -0,0 +1,463 @@
+/*
+ * kaslr_helper - helper for kaslr offset calculation
+ *
+ * Copyright (c) 2011 FUJITSU LIMITED
+ * Copyright (c) 2018 Red Hat Inc.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * Author: HATAYAMA Daisuke <d.hatayama(a)jp.fujitsu.com>
+ */
+
+#include "defs.h"
+#include <elf.h>
+#include <inttypes.h>
+
+#ifdef X86_64
+/*
+ * Get address of vector0 interrupt handler (Devide Error) from Interrupt
+ * Descriptor Table.
+ */
+static ulong
+get_vec0_addr(ulong idtr)
+{
+ struct gate_struct64 {
+ uint16_t offset_low;
+ uint16_t segment;
+ uint32_t ist : 3, zero0 : 5, type : 5, dpl : 2, p : 1;
+ uint16_t offset_middle;
+ uint32_t offset_high;
+ uint32_t zero1;
+ } __attribute__((packed)) gate;
+
+ readmem(idtr, PHYSADDR, &gate, sizeof(gate), "idt_table",
FAULT_ON_ERROR);
+
+ return ((ulong)gate.offset_high << 32)
+ + ((ulong)gate.offset_middle << 16)
+ + gate.offset_low;
+}
+
+/*
+ * Parse a string of [size[KMG] ]offset[KMG]
+ * Import from Linux kernel(lib/cmdline.c)
+ */
+static ulong memparse(char *ptr, char **retptr)
+{
+ char *endptr;
+
+ unsigned long long ret = strtoull(ptr, &endptr, 0);
+
+ switch (*endptr) {
+ case 'E':
+ case 'e':
+ ret <<= 10;
+ case 'P':
+ case 'p':
+ ret <<= 10;
+ case 'T':
+ case 't':
+ ret <<= 10;
+ case 'G':
+ case 'g':
+ ret <<= 10;
+ case 'M':
+ case 'm':
+ ret <<= 10;
+ case 'K':
+ case 'k':
+ ret <<= 10;
+ endptr++;
+ default:
+ break;
+ }
+
+ if (retptr)
+ *retptr = endptr;
+
+ return ret;
+}
+
+/*
+ * Find "elfcorehdr=" in the boot parameter of kernel and return the address
+ * of elfcorehdr.
+ */
+static ulong
+get_elfcorehdr(ulong kaslr_offset)
+{
+ char cmdline[BUFSIZE], *ptr;
+ ulong cmdline_vaddr;
+ ulong cmdline_paddr;
+ ulong buf_vaddr, buf_paddr;
+ char *end;
+ ulong elfcorehdr_addr = 0, elfcorehdr_size = 0;
+ int verbose = CRASHDEBUG(1)? 1: 0;
+
+ cmdline_vaddr = st->saved_command_line_vmlinux + kaslr_offset;
+ if (!kvtop(NULL, cmdline_vaddr, &cmdline_paddr, verbose))
+ return 0;
+
+ if (CRASHDEBUG(1)) {
+ fprintf(fp, "cmdline vaddr=%lx\n", cmdline_vaddr);
+ fprintf(fp, "cmdline paddr=%lx\n", cmdline_paddr);
+ }
+
+ if (!readmem(cmdline_paddr, PHYSADDR, &buf_vaddr, sizeof(ulong),
+ "saved_command_line", RETURN_ON_ERROR))
+ return 0;
+
+ if (!kvtop(NULL, buf_vaddr, &buf_paddr, verbose))
+ return 0;
+
+ if (CRASHDEBUG(1)) {
+ fprintf(fp, "cmdline buffer vaddr=%lx\n", buf_vaddr);
+ fprintf(fp, "cmdline buffer paddr=%lx\n", buf_paddr);
+ }
+
+ memset(cmdline, 0, BUFSIZE);
+ if (!readmem(buf_paddr, PHYSADDR, cmdline, BUFSIZE,
+ "saved_command_line", RETURN_ON_ERROR))
+ return 0;
+
+ ptr = strstr(cmdline, "elfcorehdr=");
+ if (!ptr)
+ return 0;
+
+ if (CRASHDEBUG(1))
+ fprintf(fp, "2nd kernel detected\n");
+
+ ptr += strlen("elfcorehdr=");
+ elfcorehdr_addr = memparse(ptr, &end);
+ if (*end == '@') {
+ elfcorehdr_size = elfcorehdr_addr;
+ elfcorehdr_addr = memparse(end + 1, &end);
+ }
+
+ if (CRASHDEBUG(1)) {
+ fprintf(fp, "elfcorehdr_addr=%lx\n", elfcorehdr_addr);
+ fprintf(fp, "elfcorehdr_size=%lx\n", elfcorehdr_size);
+ }
+
+ return elfcorehdr_addr;
+}
+
+ /*
+ * Get vmcoreinfo from elfcorehdr.
+ * Some codes are imported from Linux kernel(fs/proc/vmcore.c)
+ */
+static int
+get_vmcoreinfo(ulong elfcorehdr, ulong *addr, int *len)
+{
+ unsigned char e_ident[EI_NIDENT];
+ Elf64_Ehdr ehdr;
+ Elf64_Phdr phdr;
+ Elf64_Nhdr nhdr;
+ ulong ptr;
+ ulong nhdr_offset = 0;
+ int i;
+
+ if (!readmem(elfcorehdr, PHYSADDR, e_ident, EI_NIDENT,
+ "EI_NIDENT", RETURN_ON_ERROR))
+ return FALSE;
+
+ if (e_ident[EI_CLASS] != ELFCLASS64) {
+ error(INFO, "Only ELFCLASS64 is supportd\n");
+ return FALSE;
+ }
+
+ if (!readmem(elfcorehdr, PHYSADDR, &ehdr, sizeof(ehdr),
+ "Elf64_Ehdr", RETURN_ON_ERROR))
+ return FALSE;
+
+ /* Sanity Check */
+ if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0 ||
+ (ehdr.e_type != ET_CORE) ||
+ ehdr.e_ident[EI_CLASS] != ELFCLASS64 ||
+ ehdr.e_ident[EI_VERSION] != EV_CURRENT ||
+ ehdr.e_version != EV_CURRENT ||
+ ehdr.e_ehsize != sizeof(Elf64_Ehdr) ||
+ ehdr.e_phentsize != sizeof(Elf64_Phdr) ||
+ ehdr.e_phnum == 0) {
+ error(INFO, "Invalid elf header\n");
+ return FALSE;
+ }
+
+ ptr = elfcorehdr + ehdr.e_phoff;
+ for (i = 0; i < ehdr.e_phnum; i++) {
+ ulong offset;
+ char name[16];
+
+ if (!readmem(ptr, PHYSADDR, &phdr, sizeof(phdr),
+ "Elf64_Phdr", RETURN_ON_ERROR))
+ return FALSE;
+
+ ptr += sizeof(phdr);
+ if (phdr.p_type != PT_NOTE)
+ continue;
+
+ offset = phdr.p_offset;
+ if (!readmem(offset, PHYSADDR, &nhdr, sizeof(nhdr),
+ "Elf64_Nhdr", RETURN_ON_ERROR))
+ return FALSE;
+
+ offset += DIV_ROUND_UP(sizeof(Elf64_Nhdr), sizeof(Elf64_Word))*
+ sizeof(Elf64_Word);
+ memset(name, 0, sizeof(name));
+ if (!readmem(offset, PHYSADDR, name, sizeof(name),
+ "Elf64_Nhdr name", RETURN_ON_ERROR))
+ return FALSE;
+
+ if(!strcmp(name, "VMCOREINFO")) {
+ nhdr_offset = offset;
+ break;
+ }
+ }
+
+ if (!nhdr_offset)
+ return FALSE;
+
+ *addr = nhdr_offset +
+ DIV_ROUND_UP(nhdr.n_namesz, sizeof(Elf64_Word))*
+ sizeof(Elf64_Word);
+ *len = nhdr.n_descsz;
+
+ if (CRASHDEBUG(1)) {
+ fprintf(fp, "vmcoreinfo addr=%lx\n", *addr);
+ fprintf(fp, "vmcoreinfo len=%d\n", *len);
+ }
+
+ return TRUE;
+}
+
+/*
+ * Check if current kaslr_offset/phys_base is for 1st kernel or 2nd kernel.
+ * If we are in 2nd kernel, get kaslr_offset/phys_base from vmcoreinfo.
+ *
+ * 1. Get command line and try to retrieve "elfcorehdr=" boot parameter
+ * 2. If "elfcorehdr=" is not found in command line, we are in 1st kernel.
+ * There is nothing to do.
+ * 3. If "elfcorehdr=" is found, we are in 2nd kernel. Find vmcoreinfo
+ * using "elfcorehdr=" and retrieve kaslr_offset/phys_base from vmcoreinfo.
+ */
+static int
+get_kaslr_offset_from_vmcoreinfo(ulong orig_kaslr_offset,
+ ulong *kaslr_offset, ulong *phys_base)
+{
+ ulong elfcorehdr_addr = 0;
+ ulong vmcoreinfo_addr;
+ int vmcoreinfo_len;
+ char *buf, *pos;
+ int ret = FALSE;
+
+ /* Find "elfcorehdr=" in the kernel boot parameter */
+ elfcorehdr_addr = get_elfcorehdr(orig_kaslr_offset);
+ if (!elfcorehdr_addr)
+ return FALSE;
+
+ /* Get vmcoreinfo from the address of "elfcorehdr=" */
+ if (!get_vmcoreinfo(elfcorehdr_addr, &vmcoreinfo_addr, &vmcoreinfo_len))
+ return FALSE;
+
+ if (!vmcoreinfo_len)
+ return FALSE;
+
+ if (CRASHDEBUG(1))
+ fprintf(fp, "Find vmcoreinfo in kdump memory\n");
+
+ buf = GETBUF(vmcoreinfo_len);
+ if (!readmem(vmcoreinfo_addr, PHYSADDR, buf, vmcoreinfo_len,
+ "vmcoreinfo", RETURN_ON_ERROR))
+ goto quit;
+
+ /* Get phys_base form vmcoreinfo */
+ pos = strstr(buf, "NUMBER(phys_base)=");
+ if (!pos)
+ goto quit;
+ *phys_base = strtoull(pos + strlen("NUMBER(phys_base)="), NULL, 0);
+
+ /* Get kaslr_offset form vmcoreinfo */
+ pos = strstr(buf, "KERNELOFFSET=");
+ if (!pos)
+ goto quit;
+ *kaslr_offset = strtoull(pos + strlen("KERNELOFFSET="), NULL, 16);
+
+ ret = TRUE;
+
+quit:
+ FREEBUF(buf);
+ return ret;
+}
+
+/*
+ * Calculate kaslr_offset and phys_base
+ *
+ * kaslr_offset:
+ * The difference between original address in System.map or vmlinux and
+ * actual address placed randomly by kaslr feature. To be more accurate,
+ * kaslr_offset = actual address - original address
+ *
+ * phys_base:
+ * Physical address where the kerenel is placed. In other words, it's a
+ * physical address of __START_KERNEL_map. This is also decided randomly by
+ * kaslr.
+ *
+ * kaslr offset and phys_base are calculated as follows:
+ *
+ * kaslr_offset:
+ * 1) Get IDTR and CR3 value from the dump header.
+ * 2) Get a virtual address of IDT from IDTR value
+ * --- (A)
+ * 3) Translate (A) to physical address using CR3, the upper 52 bits
+ * of which points a top of page table.
+ * --- (B)
+ * 4) Get an address of vector0 (Devide Error) interrupt handler from
+ * IDT, which are pointed by (B).
+ * --- (C)
+ * 5) Get an address of symbol "divide_error" form vmlinux
+ * --- (D)
+ *
+ * Now we have two addresses:
+ * (C)-> Actual address of "divide_error"
+ * (D)-> Original address of "divide_error" in the vmlinux
+ *
+ * kaslr_offset can be calculated by the difference between these two
+ * value.
+ *
+ * phys_base;
+ * 1) Get IDT virtual address from vmlinux
+ * --- (E)
+ *
+ * So phys_base can be calculated using relationship of directly mapped
+ * address.
+ *
+ * phys_base =
+ * Physical address(B) -
+ * (Virtual address(E) + kaslr_offset - __START_KERNEL_map)
+ *
+ * Note that the address (A) cannot be used instead of (E) because (A) is
+ * not direct map address, it's a fixed map address.
+ *
+ * This solution works in most every case, but does not work in the
+ * following case.
+ *
+ * 1) If the dump is captured on early stage of kernel boot, IDTR points
+ * early IDT table(early_idts) instead of normal IDT(idt_table).
+ * 2) If the dump is captured whle kdump is working, IDTR points
+ * IDT table of 2nd kernel, not 1st kernel.
+ *
+ * Current implementation does not support the case 1), need
+ * enhancement in the future. For the case 2), get kaslr_offset and
+ * phys_base as follows.
+ *
+ * 1) Get kaslr_offset and phys_base using the above solution.
+ * 2) Get kernel boot parameter from "saved_command_line"
+ * 3) If "elfcorehdr=" is not included in boot parameter, we are in the
+ * first kernel, nothing to do any more.
+ * 4) If "elfcorehdr=" is included in boot parameter, we are in the 2nd
+ * kernel. Retrieve vmcoreinfo from address of "elfcorehdr=" and
+ * get kaslr_offset and phys_base from vmcoreinfo.
+ */
+#define PTI_USER_PGTABLE_BIT PAGE_SHIFT
+#define PTI_USER_PGTABLE_MASK (1 << PTI_USER_PGTABLE_BIT)
+#define CR3_PCID_MASK 0xFFFull
+int
+calc_kaslr_offset(ulong *kaslr_offset, ulong *phys_base)
+{
+ uint64_t cr3 = 0, idtr = 0, pgd = 0, idtr_paddr;
+ ulong divide_error_vmcore;
+ ulong kaslr_offset_kdump, phys_base_kdump;
+ int ret = FALSE;
+ int verbose = CRASHDEBUG(1)? 1: 0;
+
+ if (!machine_type("X86_64"))
+ return FALSE;
+
+ if (SADUMP_DUMPFILE()) {
+ idtr = sadump_get_idtr();
+ cr3 = sadump_get_cr3();
+ } else {
+ return FALSE;
+ }
+
+ if (st->pti_init_vmlinux || st->kaiser_init_vmlinux)
+ pgd = cr3 & ~(CR3_PCID_MASK|PTI_USER_PGTABLE_MASK);
+ else
+ pgd = cr3 & ~CR3_PCID_MASK;
+
+ /*
+ * Set up for kvtop.
+ *
+ * calc_kaslr_offset() is called before machdep_init(PRE_GDB), so some
+ * variables are not initialized yet. Set up them here to call kvtop().
+ *
+ * TODO: XEN and 5-level is not supported
+ */
+ vt->kernel_pgd[0] = pgd;
+ machdep->last_pgd_read = vt->kernel_pgd[0];
+ machdep->machspec->physical_mask_shift = __PHYSICAL_MASK_SHIFT_2_6;
+ machdep->machspec->pgdir_shift = PGDIR_SHIFT;
+ machdep->machspec->ptrs_per_pgd = PTRS_PER_PGD;
+ if (!readmem(pgd, PHYSADDR, machdep->pgd, PAGESIZE(),
+ "pgd", RETURN_ON_ERROR))
+ goto quit;
+
+ /* Convert virtual address of IDT table to physical address */
+ if (!kvtop(NULL, idtr, &idtr_paddr, verbose))
+ goto quit;
+
+ /* Now we can calculate kaslr_offset and phys_base */
+ divide_error_vmcore = get_vec0_addr(idtr_paddr);
+ *kaslr_offset = divide_error_vmcore - st->divide_error_vmlinux;
+ *phys_base = idtr_paddr -
+ (st->idt_table_vmlinux + *kaslr_offset - __START_KERNEL_map);
+
+ if (CRASHDEBUG(1)) {
+ fprintf(fp, "calc_kaslr_offset: idtr=%lx\n", idtr);
+ fprintf(fp, "calc_kaslr_offset: pgd=%lx\n", pgd);
+ fprintf(fp, "calc_kaslr_offset: idtr(phys)=%lx\n", idtr_paddr);
+ fprintf(fp, "calc_kaslr_offset: divide_error(vmlinux): %lx\n",
+ st->divide_error_vmlinux);
+ fprintf(fp, "calc_kaslr_offset: divide_error(vmcore): %lx\n",
+ divide_error_vmcore);
+ }
+
+ /*
+ * Check if current kaslr_offset/phys_base is for 1st kernel or 2nd
+ * kernel. If we are in 2nd kernel, get kaslr_offset/phys_base
+ * from vmcoreinfo
+ */
+ if (get_kaslr_offset_from_vmcoreinfo(
+ *kaslr_offset, &kaslr_offset_kdump, &phys_base_kdump)) {
+ *kaslr_offset = kaslr_offset_kdump;
+ *phys_base = phys_base_kdump;
+ } else if (CRASHDEBUG(1)) {
+ fprintf(fp, "kaslr_helper: failed to determine which kernel was running at
crash,\n");
+ fprintf(fp, "kaslr_helper: asssuming the kdump 1st kernel.\n");
+ }
+
+ if (CRASHDEBUG(1)) {
+ fprintf(fp, "calc_kaslr_offset: kaslr_offset=%lx\n",
+ *kaslr_offset);
+ fprintf(fp, "calc_kaslr_offset: phys_base=%lx\n", *phys_base);
+ }
+
+ ret = TRUE;
+quit:
+ vt->kernel_pgd[0] = 0;
+ machdep->last_pgd_read = 0;
+ return ret;
+}
+#else
+int
+calc_kaslr_offset(ulong *kaslr_offset, ulong *phys_page)
+{
+ return FALSE;
+}
+#endif /* X86_64 */
diff --git a/sadump.c b/sadump.c
index d19b40a..250d181 100644
--- a/sadump.c
+++ b/sadump.c
@@ -1569,6 +1569,13 @@ int sadump_phys_base(ulong *phys_base)
return FALSE;
}
+int sadump_set_phys_base(ulong phys_base)
+{
+ sd->phys_base = phys_base;
+
+ return TRUE;
+}
+
/*
* Used by "sys" command to show diskset disk names.
*/
@@ -1656,466 +1663,46 @@ get_sadump_data(void)
static int
get_sadump_smram_cpu_state_any(struct sadump_smram_cpu_state *smram)
{
- ulong offset;
- struct sadump_header *sh = sd->dump_header;
- int apicid;
- struct sadump_smram_cpu_state scs, zero;
-
- offset = sd->sub_hdr_offset + sizeof(uint32_t) +
- sd->dump_header->nr_cpus * sizeof(struct sadump_apic_state);
-
- memset(&zero, 0, sizeof(zero));
-
- for (apicid = 0; apicid < sh->nr_cpus; ++apicid) {
- if (!read_device(&scs, sizeof(scs), &offset)) {
- error(INFO, "sadump: cannot read sub header "
- "cpu_state\n");
- return FALSE;
- }
- if (memcmp(&scs, &zero, sizeof(scs)) != 0) {
- *smram = scs;
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-/*
- * Get address of vector0 interrupt handler (Devide Error) from Interrupt
- * Descriptor Table.
- */
-static ulong
-get_vec0_addr(ulong idtr)
-{
- struct gate_struct64 {
- uint16_t offset_low;
- uint16_t segment;
- uint32_t ist : 3, zero0 : 5, type : 5, dpl : 2, p : 1;
- uint16_t offset_middle;
- uint32_t offset_high;
- uint32_t zero1;
- } __attribute__((packed)) gate;
-
- readmem(idtr, PHYSADDR, &gate, sizeof(gate), "idt_table",
FAULT_ON_ERROR);
-
- return ((ulong)gate.offset_high << 32)
- + ((ulong)gate.offset_middle << 16)
- + gate.offset_low;
-}
-
-/*
- * Parse a string of [size[KMG] ]offset[KMG]
- * Import from Linux kernel(lib/cmdline.c)
- */
-static ulong memparse(char *ptr, char **retptr)
-{
- char *endptr;
-
- unsigned long long ret = strtoull(ptr, &endptr, 0);
-
- switch (*endptr) {
- case 'E':
- case 'e':
- ret <<= 10;
- case 'P':
- case 'p':
- ret <<= 10;
- case 'T':
- case 't':
- ret <<= 10;
- case 'G':
- case 'g':
- ret <<= 10;
- case 'M':
- case 'm':
- ret <<= 10;
- case 'K':
- case 'k':
- ret <<= 10;
- endptr++;
- default:
- break;
- }
-
- if (retptr)
- *retptr = endptr;
-
- return ret;
-}
-
-/*
- * Find "elfcorehdr=" in the boot parameter of kernel and return the address
- * of elfcorehdr.
- */
-static ulong
-get_elfcorehdr(ulong kaslr_offset)
-{
- char cmdline[BUFSIZE], *ptr;
- ulong cmdline_vaddr;
- ulong cmdline_paddr;
- ulong buf_vaddr, buf_paddr;
- char *end;
- ulong elfcorehdr_addr = 0, elfcorehdr_size = 0;
- int verbose = CRASHDEBUG(1)? 1: 0;
-
- cmdline_vaddr = st->saved_command_line_vmlinux + kaslr_offset;
- if (!kvtop(NULL, cmdline_vaddr, &cmdline_paddr, verbose))
- return 0;
-
- if (CRASHDEBUG(1)) {
- fprintf(fp, "cmdline vaddr=%lx\n", cmdline_vaddr);
- fprintf(fp, "cmdline paddr=%lx\n", cmdline_paddr);
- }
-
- if (!readmem(cmdline_paddr, PHYSADDR, &buf_vaddr, sizeof(ulong),
- "saved_command_line", RETURN_ON_ERROR))
- return 0;
-
- if (!kvtop(NULL, buf_vaddr, &buf_paddr, verbose))
- return 0;
-
- if (CRASHDEBUG(1)) {
- fprintf(fp, "cmdline buffer vaddr=%lx\n", buf_vaddr);
- fprintf(fp, "cmdline buffer paddr=%lx\n", buf_paddr);
- }
-
- memset(cmdline, 0, BUFSIZE);
- if (!readmem(buf_paddr, PHYSADDR, cmdline, BUFSIZE,
- "saved_command_line", RETURN_ON_ERROR))
- return 0;
-
- ptr = strstr(cmdline, "elfcorehdr=");
- if (!ptr)
- return 0;
-
- if (CRASHDEBUG(1))
- fprintf(fp, "2nd kernel detected\n");
-
- ptr += strlen("elfcorehdr=");
- elfcorehdr_addr = memparse(ptr, &end);
- if (*end == '@') {
- elfcorehdr_size = elfcorehdr_addr;
- elfcorehdr_addr = memparse(end + 1, &end);
- }
-
- if (CRASHDEBUG(1)) {
- fprintf(fp, "elfcorehdr_addr=%lx\n", elfcorehdr_addr);
- fprintf(fp, "elfcorehdr_size=%lx\n", elfcorehdr_size);
- }
-
- return elfcorehdr_addr;
-}
-
- /*
- * Get vmcoreinfo from elfcorehdr.
- * Some codes are imported from Linux kernel(fs/proc/vmcore.c)
- */
-static int
-get_vmcoreinfo(ulong elfcorehdr, ulong *addr, int *len)
-{
- unsigned char e_ident[EI_NIDENT];
- Elf64_Ehdr ehdr;
- Elf64_Phdr phdr;
- Elf64_Nhdr nhdr;
- ulong ptr;
- ulong nhdr_offset = 0;
- int i;
-
- if (!readmem(elfcorehdr, PHYSADDR, e_ident, EI_NIDENT,
- "EI_NIDENT", RETURN_ON_ERROR))
- return FALSE;
-
- if (e_ident[EI_CLASS] != ELFCLASS64) {
- error(INFO, "Only ELFCLASS64 is supportd\n");
- return FALSE;
- }
-
- if (!readmem(elfcorehdr, PHYSADDR, &ehdr, sizeof(ehdr),
- "Elf64_Ehdr", RETURN_ON_ERROR))
- return FALSE;
-
- /* Sanity Check */
- if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0 ||
- (ehdr.e_type != ET_CORE) ||
- ehdr.e_ident[EI_CLASS] != ELFCLASS64 ||
- ehdr.e_ident[EI_VERSION] != EV_CURRENT ||
- ehdr.e_version != EV_CURRENT ||
- ehdr.e_ehsize != sizeof(Elf64_Ehdr) ||
- ehdr.e_phentsize != sizeof(Elf64_Phdr) ||
- ehdr.e_phnum == 0) {
- error(INFO, "Invalid elf header\n");
- return FALSE;
- }
-
- ptr = elfcorehdr + ehdr.e_phoff;
- for (i = 0; i < ehdr.e_phnum; i++) {
- ulong offset;
- char name[16];
-
- if (!readmem(ptr, PHYSADDR, &phdr, sizeof(phdr),
- "Elf64_Phdr", RETURN_ON_ERROR))
- return FALSE;
-
- ptr += sizeof(phdr);
- if (phdr.p_type != PT_NOTE)
- continue;
-
- offset = phdr.p_offset;
- if (!readmem(offset, PHYSADDR, &nhdr, sizeof(nhdr),
- "Elf64_Nhdr", RETURN_ON_ERROR))
- return FALSE;
-
- offset += DIV_ROUND_UP(sizeof(Elf64_Nhdr), sizeof(Elf64_Word))*
- sizeof(Elf64_Word);
- memset(name, 0, sizeof(name));
- if (!readmem(offset, PHYSADDR, name, sizeof(name),
- "Elf64_Nhdr name", RETURN_ON_ERROR))
- return FALSE;
-
- if(!strcmp(name, "VMCOREINFO")) {
- nhdr_offset = offset;
- break;
- }
- }
-
- if (!nhdr_offset)
- return FALSE;
-
- *addr = nhdr_offset +
- DIV_ROUND_UP(nhdr.n_namesz, sizeof(Elf64_Word))*
- sizeof(Elf64_Word);
- *len = nhdr.n_descsz;
-
- if (CRASHDEBUG(1)) {
- fprintf(fp, "vmcoreinfo addr=%lx\n", *addr);
- fprintf(fp, "vmcoreinfo len=%d\n", *len);
- }
-
- return TRUE;
+ ulong offset;
+ struct sadump_header *sh = sd->dump_header;
+ int apicid;
+ struct sadump_smram_cpu_state scs, zero;
+
+ offset = sd->sub_hdr_offset + sizeof(uint32_t) +
+ sd->dump_header->nr_cpus * sizeof(struct sadump_apic_state);
+
+ memset(&zero, 0, sizeof(zero));
+
+ for (apicid = 0; apicid < sh->nr_cpus; ++apicid) {
+ if (!read_device(&scs, sizeof(scs), &offset)) {
+ error(INFO, "sadump: cannot read sub header "
+ "cpu_state\n");
+ return FALSE;
+ }
+ if (memcmp(&scs, &zero, sizeof(scs)) != 0) {
+ *smram = scs;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
}
-/*
- * Check if current kaslr_offset/phys_base is for 1st kernel or 2nd kernel.
- * If we are in 2nd kernel, get kaslr_offset/phys_base from vmcoreinfo.
- *
- * 1. Get command line and try to retrieve "elfcorehdr=" boot parameter
- * 2. If "elfcorehdr=" is not found in command line, we are in 1st kernel.
- * There is nothing to do.
- * 3. If "elfcorehdr=" is found, we are in 2nd kernel. Find vmcoreinfo
- * using "elfcorehdr=" and retrieve kaslr_offset/phys_base from vmcoreinfo.
- */
-static int
-get_kaslr_offset_from_vmcoreinfo(ulong orig_kaslr_offset,
- ulong *kaslr_offset, ulong *phys_base)
+ulong sadump_get_cr3()
{
- ulong elfcorehdr_addr = 0;
- ulong vmcoreinfo_addr;
- int vmcoreinfo_len;
- char *buf, *pos;
- int ret = FALSE;
-
- /* Find "elfcorehdr=" in the kernel boot parameter */
- elfcorehdr_addr = get_elfcorehdr(orig_kaslr_offset);
- if (!elfcorehdr_addr)
- return FALSE;
-
- /* Get vmcoreinfo from the address of "elfcorehdr=" */
- if (!get_vmcoreinfo(elfcorehdr_addr, &vmcoreinfo_addr, &vmcoreinfo_len))
- return FALSE;
-
- if (!vmcoreinfo_len)
- return FALSE;
+ struct sadump_smram_cpu_state scs;
- if (CRASHDEBUG(1))
- fprintf(fp, "Find vmcoreinfo in kdump memory\n");
-
- buf = GETBUF(vmcoreinfo_len);
- if (!readmem(vmcoreinfo_addr, PHYSADDR, buf, vmcoreinfo_len,
- "vmcoreinfo", RETURN_ON_ERROR))
- goto quit;
-
- /* Get phys_base form vmcoreinfo */
- pos = strstr(buf, "NUMBER(phys_base)=");
- if (!pos)
- goto quit;
- *phys_base = strtoull(pos + strlen("NUMBER(phys_base)="), NULL, 0);
-
- /* Get kaslr_offset form vmcoreinfo */
- pos = strstr(buf, "KERNELOFFSET=");
- if (!pos)
- goto quit;
- *kaslr_offset = strtoull(pos + strlen("KERNELOFFSET="), NULL, 16);
-
- ret = TRUE;
-
-quit:
- FREEBUF(buf);
- return ret;
+ memset(&scs, 0, sizeof(scs));
+ get_sadump_smram_cpu_state_any(&scs);
+ return scs.Cr3;
}
-/*
- * Calculate kaslr_offset and phys_base
- *
- * kaslr_offset:
- * The difference between original address in System.map or vmlinux and
- * actual address placed randomly by kaslr feature. To be more accurate,
- * kaslr_offset = actual address - original address
- *
- * phys_base:
- * Physical address where the kerenel is placed. In other words, it's a
- * physical address of __START_KERNEL_map. This is also decided randomly by
- * kaslr.
- *
- * kaslr offset and phys_base are calculated as follows:
- *
- * kaslr_offset:
- * 1) Get IDTR and CR3 value from the dump header.
- * 2) Get a virtual address of IDT from IDTR value
- * --- (A)
- * 3) Translate (A) to physical address using CR3, the upper 52 bits
- * of which points a top of page table.
- * --- (B)
- * 4) Get an address of vector0 (Devide Error) interrupt handler from
- * IDT, which are pointed by (B).
- * --- (C)
- * 5) Get an address of symbol "divide_error" form vmlinux
- * --- (D)
- *
- * Now we have two addresses:
- * (C)-> Actual address of "divide_error"
- * (D)-> Original address of "divide_error" in the vmlinux
- *
- * kaslr_offset can be calculated by the difference between these two
- * value.
- *
- * phys_base;
- * 1) Get IDT virtual address from vmlinux
- * --- (E)
- *
- * So phys_base can be calculated using relationship of directly mapped
- * address.
- *
- * phys_base =
- * Physical address(B) -
- * (Virtual address(E) + kaslr_offset - __START_KERNEL_map)
- *
- * Note that the address (A) cannot be used instead of (E) because (A) is
- * not direct map address, it's a fixed map address.
- *
- * This solution works in most every case, but does not work in the
- * following case.
- *
- * 1) If the dump is captured on early stage of kernel boot, IDTR points
- * early IDT table(early_idts) instead of normal IDT(idt_table).
- * 2) If the dump is captured whle kdump is working, IDTR points
- * IDT table of 2nd kernel, not 1st kernel.
- *
- * Current implementation does not support the case 1), need
- * enhancement in the future. For the case 2), get kaslr_offset and
- * phys_base as follows.
- *
- * 1) Get kaslr_offset and phys_base using the above solution.
- * 2) Get kernel boot parameter from "saved_command_line"
- * 3) If "elfcorehdr=" is not included in boot parameter, we are in the
- * first kernel, nothing to do any more.
- * 4) If "elfcorehdr=" is included in boot parameter, we are in the 2nd
- * kernel. Retrieve vmcoreinfo from address of "elfcorehdr=" and
- * get kaslr_offset and phys_base from vmcoreinfo.
- */
-#define PTI_USER_PGTABLE_BIT PAGE_SHIFT
-#define PTI_USER_PGTABLE_MASK (1 << PTI_USER_PGTABLE_BIT)
-#define CR3_PCID_MASK 0xFFFull
-int
-sadump_calc_kaslr_offset(ulong *kaslr_offset)
+ulong sadump_get_idtr()
{
- ulong phys_base = 0;
struct sadump_smram_cpu_state scs;
- uint64_t idtr = 0, pgd = 0, idtr_paddr;
- ulong divide_error_vmcore;
- ulong kaslr_offset_kdump, phys_base_kdump;
- int ret = FALSE;
- int verbose = CRASHDEBUG(1)? 1: 0;
-
- if (!machine_type("X86_64"))
- return FALSE;
memset(&scs, 0, sizeof(scs));
get_sadump_smram_cpu_state_any(&scs);
- if (st->pti_init_vmlinux || st->kaiser_init_vmlinux)
- pgd = scs.Cr3 & ~(CR3_PCID_MASK|PTI_USER_PGTABLE_MASK);
- else
- pgd = scs.Cr3 & ~CR3_PCID_MASK;
- idtr = ((uint64_t)scs.IdtUpper)<<32 | (uint64_t)scs.IdtLower;
-
- /*
- * Set up for kvtop.
- *
- * calc_kaslr_offset() is called before machdep_init(PRE_GDB), so some
- * variables are not initialized yet. Set up them here to call kvtop().
- *
- * TODO: XEN and 5-level is not supported
- */
- vt->kernel_pgd[0] = pgd;
- machdep->last_pgd_read = vt->kernel_pgd[0];
- machdep->machspec->physical_mask_shift = __PHYSICAL_MASK_SHIFT_2_6;
- machdep->machspec->pgdir_shift = PGDIR_SHIFT;
- machdep->machspec->ptrs_per_pgd = PTRS_PER_PGD;
- if (!readmem(pgd, PHYSADDR, machdep->pgd, PAGESIZE(),
- "pgd", RETURN_ON_ERROR))
- goto quit;
-
- /* Convert virtual address of IDT table to physical address */
- if (!kvtop(NULL, idtr, &idtr_paddr, verbose))
- goto quit;
-
- /* Now we can calculate kaslr_offset and phys_base */
- divide_error_vmcore = get_vec0_addr(idtr_paddr);
- *kaslr_offset = divide_error_vmcore - st->divide_error_vmlinux;
- phys_base = idtr_paddr -
- (st->idt_table_vmlinux + *kaslr_offset - __START_KERNEL_map);
-
- if (CRASHDEBUG(1)) {
- fprintf(fp, "calc_kaslr_offset: idtr=%lx\n", idtr);
- fprintf(fp, "calc_kaslr_offset: pgd=%lx\n", pgd);
- fprintf(fp, "calc_kaslr_offset: idtr(phys)=%lx\n", idtr_paddr);
- fprintf(fp, "calc_kaslr_offset: divide_error(vmlinux): %lx\n",
- st->divide_error_vmlinux);
- fprintf(fp, "calc_kaslr_offset: divide_error(vmcore): %lx\n",
- divide_error_vmcore);
- }
-
- /*
- * Check if current kaslr_offset/phys_base is for 1st kernel or 2nd
- * kernel. If we are in 2nd kernel, get kaslr_offset/phys_base
- * from vmcoreinfo
- */
- if (get_kaslr_offset_from_vmcoreinfo(
- *kaslr_offset, &kaslr_offset_kdump, &phys_base_kdump)) {
- *kaslr_offset = kaslr_offset_kdump;
- phys_base = phys_base_kdump;
- } else if (CRASHDEBUG(1)) {
- fprintf(fp, "sadump: failed to determine which kernel was running at
crash,\n");
- fprintf(fp, "sadump: asssuming the kdump 1st kernel.\n");
- }
-
- if (CRASHDEBUG(1)) {
- fprintf(fp, "calc_kaslr_offset: kaslr_offset=%lx\n",
- *kaslr_offset);
- fprintf(fp, "calc_kaslr_offset: phys_base=%lx\n", phys_base);
- }
-
- sd->phys_base = phys_base;
- ret = TRUE;
-quit:
- vt->kernel_pgd[0] = 0;
- machdep->last_pgd_read = 0;
- return ret;
-}
-#else
-int
-sadump_calc_kaslr_offset(ulong *kaslr_offset)
-{
- return FALSE;
+ return ((uint64_t)scs.IdtUpper)<<32 | (uint64_t)scs.IdtLower;
}
#endif /* X86_64 */
diff --git a/symbols.c b/symbols.c
index 7910f53..54aa5b2 100644
--- a/symbols.c
+++ b/symbols.c
@@ -642,14 +642,18 @@ derive_kaslr_offset(bfd *abfd, int dynamic, bfd_byte *start,
bfd_byte *end,
if (SADUMP_DUMPFILE()) {
ulong kaslr_offset = 0;
+ ulong phys_base = 0;
- sadump_calc_kaslr_offset(&kaslr_offset);
+ calc_kaslr_offset(&kaslr_offset, &phys_base);
if (kaslr_offset) {
kt->relocate = kaslr_offset * -1;
kt->flags |= RELOC_SET;
}
+ if (phys_base)
+ sadump_set_phys_base(phys_base);
+
return;
}
--
2.14.3