[Crash-utility] [PATCH v3 1/4] Move kaslr related functions from sadump.c to kaslr_helper.c

Hatayama, Daisuke d.hatayama at jp.fujitsu.com
Thu Mar 22 06:38:43 UTC 2018


> -----Original Message-----
> From: crash-utility-bounces at redhat.com
> [mailto:crash-utility-bounces at redhat.com] On Behalf Of Sergio Lopez
> Sent: Tuesday, March 20, 2018 9:49 PM
> To: crash-utility at 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 at 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 at redhat.com
> https://www.redhat.com/mailman/listinfo/crash-utility





More information about the Crash-utility mailing list