-----Original Message-----
 From: crash-utility-bounces(a)redhat.com
 [mailto:crash-utility-bounces@redhat.com] On Behalf Of Sergio Lopez
 Sent: Tuesday, March 20, 2018 9:49 PM
 To: crash-utility(a)redhat.com
 Subject: [Crash-utility] [PATCH v3 1/4] Move kaslr related functions from
 sadump.c to kaslr_helper.c
 
 Those functions are going to be used for netdumps too.
 ---
  Makefile       |   7 +-
  defs.h         |   7 +
  kaslr_helper.c | 460
 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  sadump.c       | 486
 ++++-----------------------------------------------------
  symbols.c      |   6 +-
  5 files changed, 511 insertions(+), 455 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..808ac61 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,7 @@ void sadump_unset_zero_excluded(void);
  struct sadump_data;
  struct sadump_data *get_sadump_data(void);
  int sadump_calc_kaslr_offset(ulong *);
 +int sadump_get_cr3_idtr(ulong *, ulong *);
 
  /*
   * qemu.c
 @@ -6389,6 +6391,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..65256ab
 --- /dev/null
 +++ b/kaslr_helper.c
 @@ -0,0 +1,460 @@
 +/*
 + * 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> 
Indoh-san, too? How do you think Indoh-san? :)
Sergio, it seems good to me in my first look, but please wait until next week
since I need to prepare sadump dump files for test from now on because I don't
have them now.
 + */
 +
 +#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() && !sadump_get_cr3_idtr(&cr3, &idtr)) {
 +		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..a37602b 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,41 @@ 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;
 -}
 -
 -/*
 - * 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;
 +        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;
  }
 
 -/*
 - * 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)
 +int sadump_get_cr3_idtr(ulong *cr3, ulong *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");
 -	}
 +	*cr3 = scs.Cr3;
 +	*idtr = ((uint64_t)scs.IdtUpper)<<32 | (uint64_t)scs.IdtLower;
 
 -	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 TRUE;
  }
  #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
 
 --
 Crash-utility mailing list
 Crash-utility(a)redhat.com
 
https://www.redhat.com/mailman/listinfo/crash-utility