[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