mirror of https://github.com/crash-utility/crash
807 lines
20 KiB
C
807 lines
20 KiB
C
/* snap.c - capture live memory into a kdump or netdump dumpfile
|
|
*
|
|
* Copyright (C) 2009, 2013, 2014, 2017 David Anderson
|
|
* Copyright (C) 2009, 2013, 2014, 2017 Red Hat, Inc. All rights reserved.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include "defs.h"
|
|
#include <sys/time.h>
|
|
#include <linux/types.h>
|
|
#include <elf.h>
|
|
|
|
void snap_init(void);
|
|
void snap_fini(void);
|
|
|
|
void cmd_snap(void);
|
|
char *help_snap[];
|
|
|
|
static struct command_table_entry command_table[] = {
|
|
{ "snap", cmd_snap, help_snap, 0 },
|
|
{ NULL }
|
|
};
|
|
|
|
static char *generate_elf_header(int, int, char *);
|
|
static int verify_paddr(physaddr_t);
|
|
static void init_ram_segments(void);
|
|
static int print_progress(const char *, ulong);
|
|
|
|
#if defined(X86) || defined(X86_64) || defined(IA64) || defined(PPC64) || defined(ARM64)
|
|
int supported = TRUE;
|
|
#else
|
|
int supported = FALSE;
|
|
#endif
|
|
|
|
void __attribute__((constructor))
|
|
snap_init(void) /* Register the command set. */
|
|
{
|
|
register_extension(command_table);
|
|
}
|
|
|
|
void __attribute__((destructor))
|
|
snap_fini(void)
|
|
{
|
|
}
|
|
|
|
|
|
/*
|
|
* Just pass in an unused filename.
|
|
*/
|
|
void
|
|
cmd_snap(void)
|
|
{
|
|
int c, fd, n;
|
|
physaddr_t paddr;
|
|
size_t offset;
|
|
char *buf;
|
|
char *filename;
|
|
struct node_table *nt;
|
|
int type;
|
|
char *elf_header;
|
|
Elf64_Phdr *load;
|
|
int load_index;
|
|
|
|
if (!supported)
|
|
error(FATAL, "command not supported on the %s architecture\n",
|
|
pc->machine_type);
|
|
|
|
filename = NULL;
|
|
buf = GETBUF(PAGESIZE());
|
|
type = KDUMP_ELF64;
|
|
|
|
while ((c = getopt(argcnt, args, "n")) != EOF) {
|
|
switch(c)
|
|
{
|
|
case 'n':
|
|
if (machine_type("X86_64"))
|
|
option_not_supported('n');
|
|
else
|
|
type = NETDUMP_ELF64;
|
|
break;
|
|
default:
|
|
argerrs++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (argerrs || !args[optind])
|
|
cmd_usage(pc->curcmd, SYNOPSIS);
|
|
|
|
while (args[optind]) {
|
|
if (filename)
|
|
cmd_usage(pc->curcmd, SYNOPSIS);
|
|
|
|
if (file_exists(args[optind], NULL))
|
|
error(FATAL, "%s: file already exists\n", args[optind]);
|
|
else if ((fd = open(args[optind], O_RDWR|O_CREAT, 0644)) < 0)
|
|
error(FATAL, args[optind]);
|
|
|
|
filename = args[optind];
|
|
optind++;
|
|
}
|
|
|
|
if (!filename)
|
|
cmd_usage(pc->curcmd, SYNOPSIS);
|
|
|
|
init_ram_segments();
|
|
|
|
if (!(elf_header = generate_elf_header(type, fd, filename)))
|
|
error(FATAL, "cannot generate ELF header\n");
|
|
|
|
load = (Elf64_Phdr *)(elf_header + sizeof(Elf64_Ehdr) + sizeof(Elf64_Phdr));
|
|
load_index = machine_type("X86_64") || machine_type("IA64") ? 1 : 0;
|
|
|
|
for (n = 0; n < vt->numnodes; n++) {
|
|
nt = &vt->node_table[n];
|
|
paddr = nt->start_paddr;
|
|
offset = load[load_index + n].p_offset;
|
|
|
|
for (c = 0; c < nt->size; c++, paddr += PAGESIZE()) {
|
|
if (!verify_paddr(paddr))
|
|
continue;
|
|
if (!readmem(paddr, PHYSADDR, &buf[0], PAGESIZE(),
|
|
"memory page", QUIET|RETURN_ON_ERROR))
|
|
continue;
|
|
|
|
lseek(fd, (off_t)(paddr + offset - nt->start_paddr), SEEK_SET);
|
|
if (write(fd, &buf[0], PAGESIZE()) != PAGESIZE())
|
|
error(FATAL, "write to dumpfile failed\n");
|
|
|
|
if (!print_progress(filename, BTOP(paddr)))
|
|
return;
|
|
}
|
|
}
|
|
|
|
fprintf(stderr, "\r%s: [100%%] ", filename);
|
|
fprintf(fp, "\n");
|
|
sprintf(buf, "/bin/ls -l %s\n", filename);
|
|
system(buf);
|
|
|
|
FREEBUF(elf_header);
|
|
FREEBUF(buf);
|
|
}
|
|
|
|
|
|
char *help_snap[] = {
|
|
"snap", /* command name */
|
|
"take a memory snapshot", /* short description */
|
|
"[-n] dumpfile", /* filename */
|
|
|
|
" This command takes a snapshot of physical memory and creates an ELF vmcore.",
|
|
" The default vmcore is a kdump-style dumpfile. Supported on x86, x86_64,",
|
|
" ia64 and ppc64 architectures only.",
|
|
" ",
|
|
" -n create a netdump-style vmcore (n/a on x86_64).",
|
|
NULL
|
|
};
|
|
|
|
/*
|
|
* Architecture-specific and -generic ELF header data borrowed from the
|
|
* netdump.h file in the netdump package, modified slightly to also create
|
|
* a kdump-style vmcore.
|
|
*/
|
|
|
|
/******************************************************************************
|
|
* Elf core dumping *
|
|
******************************************************************************/
|
|
|
|
/*
|
|
* Host-platform independent data
|
|
*/
|
|
#define ELF_PRARGSZ (80) /* Number of chars for args */
|
|
struct elf_prpsinfo_64
|
|
{
|
|
char pr_state; /* numeric process state */
|
|
char pr_sname; /* char for pr_state */
|
|
char pr_zomb; /* zombie */
|
|
char pr_nice; /* nice val */
|
|
__u64 pr_flag; /* flags */
|
|
__u32 pr_uid;
|
|
__u32 pr_gid;
|
|
__u32 pr_pid, pr_ppid, pr_pgrp, pr_sid;
|
|
/* Lots missing */
|
|
char pr_fname[16]; /* filename of executable */
|
|
char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */
|
|
};
|
|
|
|
/*
|
|
* i386 specific
|
|
*/
|
|
struct user_regs_struct_i386 {
|
|
__u32 ebx, ecx, edx, esi, edi, ebp, eax;
|
|
__u16 ds, __ds, es, __es;
|
|
__u16 fs, __fs, gs, __gs;
|
|
__u32 orig_eax, eip;
|
|
__u16 cs, __cs;
|
|
__u32 eflags, esp;
|
|
__u16 ss, __ss;
|
|
};
|
|
|
|
#define ELF_NGREG_I386 (sizeof (struct user_regs_struct_i386) / sizeof(__u32))
|
|
typedef __u32 elf_gregset_i386_t[ELF_NGREG_I386];
|
|
|
|
struct elf_prstatus_i386 {
|
|
char pad[72];
|
|
elf_gregset_i386_t pr_reg; /* GP registers */
|
|
__u32 pr_fpvalid; /* True if math co-processor being used. */
|
|
};
|
|
|
|
/*
|
|
* x86_64 specific
|
|
*/
|
|
struct user_regs_struct_x86_64 {
|
|
__u64 r15,r14,r13,r12,rbp,rbx,r11,r10;
|
|
__u64 r9,r8,rax,rcx,rdx,rsi,rdi,orig_rax;
|
|
__u64 rip,cs,eflags;
|
|
__u64 rsp,ss;
|
|
__u64 fs_base, gs_base;
|
|
__u64 ds,es,fs,gs;
|
|
};
|
|
|
|
#define ELF_NGREG_X86_64 (sizeof (struct user_regs_struct_x86_64) / sizeof(__u64))
|
|
typedef __u64 elf_gregset_x86_64_t[ELF_NGREG_X86_64];
|
|
|
|
struct elf_prstatus_x86_64 {
|
|
char pad[112];
|
|
elf_gregset_x86_64_t pr_reg; /* GP registers */
|
|
__u32 pr_fpvalid; /* True if math co-processor being used. */
|
|
};
|
|
|
|
/*
|
|
* ppc64 specific
|
|
*/
|
|
struct user_regs_struct_ppc64 {
|
|
__u64 gpr[32];
|
|
__u64 nip;
|
|
__u64 msr;
|
|
__u64 orig_gpr3;
|
|
__u64 ctr;
|
|
__u64 link;
|
|
__u64 xer;
|
|
__u64 ccr;
|
|
__u64 softe;
|
|
__u64 trap;
|
|
__u64 dar;
|
|
__u64 dsisr;
|
|
__u64 result;
|
|
};
|
|
|
|
#define ELF_NGREG_PPC64 (sizeof (struct user_regs_struct_ppc64) / sizeof(__u64))
|
|
typedef __u64 elf_gregset_ppc64_t[ELF_NGREG_PPC64];
|
|
|
|
struct elf_prstatus_ppc64 {
|
|
char pad[112];
|
|
elf_gregset_ppc64_t pr_reg; /* GP registers */
|
|
__u32 pr_fpvalid; /* True if math co-processor being used. */
|
|
};
|
|
|
|
/*
|
|
* ia64 specific
|
|
*/
|
|
struct _ia64_fpreg {
|
|
union {
|
|
__u64 bits[2];
|
|
} u;
|
|
} __attribute__ ((aligned (16)));
|
|
|
|
struct user_regs_struct_ia64 {
|
|
/* The following registers are saved by SAVE_MIN: */
|
|
__u64 b6; /* scratch */
|
|
__u64 b7; /* scratch */
|
|
|
|
__u64 ar_csd; /* used by cmp8xchg16 (scratch) */
|
|
__u64 ar_ssd; /* reserved for future use (scratch) */
|
|
|
|
__u64 r8; /* scratch (return value register 0) */
|
|
__u64 r9; /* scratch (return value register 1) */
|
|
__u64 r10; /* scratch (return value register 2) */
|
|
__u64 r11; /* scratch (return value register 3) */
|
|
|
|
__u64 cr_ipsr; /* interrupted task's psr */
|
|
__u64 cr_iip; /* interrupted task's instruction pointer */
|
|
__u64 cr_ifs; /* interrupted task's function state */
|
|
|
|
__u64 ar_unat; /* interrupted task's NaT register (preserved) */
|
|
__u64 ar_pfs; /* prev function state */
|
|
__u64 ar_rsc; /* RSE configuration */
|
|
/* The following two are valid only if cr_ipsr.cpl > 0: */
|
|
__u64 ar_rnat; /* RSE NaT */
|
|
__u64 ar_bspstore; /* RSE bspstore */
|
|
|
|
__u64 pr; /* 64 predicate registers (1 bit each) */
|
|
__u64 b0; /* return pointer (bp) */
|
|
__u64 loadrs; /* size of dirty partition << 16 */
|
|
|
|
__u64 r1; /* the gp pointer */
|
|
__u64 r12; /* interrupted task's memory stack pointer */
|
|
__u64 r13; /* thread pointer */
|
|
|
|
__u64 ar_fpsr; /* floating point status (preserved) */
|
|
__u64 r15; /* scratch */
|
|
|
|
/* The remaining registers are NOT saved for system calls. */
|
|
|
|
__u64 r14; /* scratch */
|
|
__u64 r2; /* scratch */
|
|
__u64 r3; /* scratch */
|
|
|
|
/* The following registers are saved by SAVE_REST: */
|
|
__u64 r16; /* scratch */
|
|
__u64 r17; /* scratch */
|
|
__u64 r18; /* scratch */
|
|
__u64 r19; /* scratch */
|
|
__u64 r20; /* scratch */
|
|
__u64 r21; /* scratch */
|
|
__u64 r22; /* scratch */
|
|
__u64 r23; /* scratch */
|
|
__u64 r24; /* scratch */
|
|
__u64 r25; /* scratch */
|
|
__u64 r26; /* scratch */
|
|
__u64 r27; /* scratch */
|
|
__u64 r28; /* scratch */
|
|
__u64 r29; /* scratch */
|
|
__u64 r30; /* scratch */
|
|
__u64 r31; /* scratch */
|
|
|
|
__u64 ar_ccv; /* compare/exchange value (scratch) */
|
|
|
|
/*
|
|
* Floating point registers that the kernel considers scratch:
|
|
*/
|
|
struct _ia64_fpreg f6; /* scratch */
|
|
struct _ia64_fpreg f7; /* scratch */
|
|
struct _ia64_fpreg f8; /* scratch */
|
|
struct _ia64_fpreg f9; /* scratch */
|
|
struct _ia64_fpreg f10; /* scratch */
|
|
struct _ia64_fpreg f11; /* scratch */
|
|
};
|
|
|
|
#define ELF_NGREG_IA64 (sizeof (struct user_regs_struct_ia64) / sizeof(__u64))
|
|
typedef __u64 elf_gregset_ia64_t[ELF_NGREG_IA64];
|
|
|
|
struct elf_prstatus_ia64 {
|
|
char pad[112];
|
|
elf_gregset_ia64_t pr_reg; /* GP registers */
|
|
__u32 pr_fpvalid; /* True if math co-processor being used. */
|
|
};
|
|
|
|
/*
|
|
* arm64 specific
|
|
*/
|
|
|
|
struct user_pt_regs_arm64 {
|
|
__u64 regs[31];
|
|
__u64 sp;
|
|
__u64 pc;
|
|
__u64 pstate;
|
|
};
|
|
|
|
#define ELF_NGREG_ARM64 (sizeof (struct user_pt_regs_arm64) / sizeof(elf_greg_t))
|
|
#ifndef elf_greg_t
|
|
typedef unsigned long elf_greg_t;
|
|
#endif
|
|
typedef elf_greg_t elf_gregset_arm64_t[ELF_NGREG_ARM64];
|
|
|
|
struct elf_prstatus_arm64 {
|
|
char pad[112];
|
|
elf_gregset_arm64_t pr_reg;
|
|
int pr_fpvalid;
|
|
};
|
|
|
|
|
|
union prstatus {
|
|
struct elf_prstatus_i386 x86;
|
|
struct elf_prstatus_x86_64 x86_64;
|
|
struct elf_prstatus_ppc64 ppc64;
|
|
struct elf_prstatus_ia64 ia64;
|
|
struct elf_prstatus_arm64 arm64;
|
|
};
|
|
|
|
static size_t
|
|
dump_elf_note(char *buf, Elf64_Word type, char *name, char *desc, int d_len)
|
|
{
|
|
Elf64_Nhdr *note;
|
|
size_t len;
|
|
|
|
note = (Elf64_Nhdr *)buf;
|
|
note->n_namesz = strlen(name);
|
|
note->n_descsz = d_len;
|
|
note->n_type = type;
|
|
len = sizeof(Elf64_Nhdr);
|
|
|
|
memcpy(buf + len, name, note->n_namesz);
|
|
len = roundup(len + note->n_namesz, 4);
|
|
|
|
memcpy(buf + len, desc, note->n_descsz);
|
|
len = roundup(len + note->n_descsz, 4);
|
|
|
|
return len;
|
|
}
|
|
|
|
char *
|
|
generate_elf_header(int type, int fd, char *filename)
|
|
{
|
|
int i, n;
|
|
char *buffer, *ptr;
|
|
Elf64_Ehdr *elf;
|
|
Elf64_Phdr *notes;
|
|
Elf64_Phdr *load;
|
|
size_t offset, len, l_offset;
|
|
size_t data_offset;
|
|
struct elf_prpsinfo_64 prpsinfo;
|
|
union prstatus prstatus;
|
|
int prstatus_len;
|
|
ushort e_machine;
|
|
int num_segments;
|
|
struct node_table *nt;
|
|
struct SNAP_info {
|
|
ulonglong task_struct;
|
|
ulonglong arch_data1;
|
|
ulonglong arch_data2;
|
|
} SNAP_info;
|
|
|
|
num_segments = vt->numnodes;
|
|
|
|
if (machine_type("X86_64")) {
|
|
e_machine = EM_X86_64;
|
|
prstatus_len = sizeof(prstatus.x86_64);
|
|
num_segments += 1; /* mapped kernel section for phys_base */
|
|
} else if (machine_type("X86")) {
|
|
e_machine = EM_386;
|
|
prstatus_len = sizeof(prstatus.x86);
|
|
} else if (machine_type("IA64")) {
|
|
e_machine = EM_IA_64;
|
|
prstatus_len = sizeof(prstatus.ia64);
|
|
num_segments += 1; /* mapped kernel section for phys_start */
|
|
} else if (machine_type("PPC64")) {
|
|
e_machine = EM_PPC64;
|
|
prstatus_len = sizeof(prstatus.ppc64);
|
|
} else if (machine_type("ARM64")) {
|
|
e_machine = EM_AARCH64;
|
|
prstatus_len = sizeof(prstatus.arm64);
|
|
} else
|
|
return NULL;
|
|
|
|
/* should be enought for the notes + roundup + two blocks */
|
|
buffer = (char *)GETBUF(sizeof(Elf64_Ehdr) +
|
|
num_segments * sizeof(Elf64_Phdr) + PAGESIZE() * 2);
|
|
offset = 0;
|
|
ptr = buffer;
|
|
|
|
/* Elf header */
|
|
elf = (Elf64_Ehdr *)ptr;
|
|
memcpy(elf->e_ident, ELFMAG, SELFMAG);
|
|
elf->e_ident[EI_CLASS] = ELFCLASS64;
|
|
#if __BYTE_ORDER == __BIG_ENDIAN
|
|
elf->e_ident[EI_DATA] = ELFDATA2MSB;
|
|
#else
|
|
elf->e_ident[EI_DATA] = ELFDATA2LSB;
|
|
#endif
|
|
elf->e_ident[EI_VERSION] = EV_CURRENT;
|
|
elf->e_ident[EI_OSABI] = ELFOSABI_SYSV;
|
|
elf->e_ident[EI_ABIVERSION] = 0;
|
|
memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
|
|
|
|
elf->e_type = ET_CORE;
|
|
elf->e_machine = e_machine;
|
|
elf->e_version = EV_CURRENT;
|
|
elf->e_entry = 0;
|
|
elf->e_phoff = sizeof(Elf64_Ehdr);
|
|
elf->e_shoff = 0;
|
|
elf->e_flags = 0;
|
|
elf->e_ehsize = sizeof(Elf64_Ehdr);
|
|
elf->e_phentsize = sizeof(Elf64_Phdr);
|
|
elf->e_phnum = 1 + num_segments;
|
|
elf->e_shentsize = 0;
|
|
elf->e_shnum = 0;
|
|
elf->e_shstrndx = 0;
|
|
|
|
offset += sizeof(Elf64_Ehdr);
|
|
ptr += sizeof(Elf64_Ehdr);
|
|
|
|
/* PT_NOTE */
|
|
notes = (Elf64_Phdr *)ptr;
|
|
notes->p_type = PT_NOTE;
|
|
notes->p_offset = 0; /* TO BE FILLED IN */
|
|
notes->p_vaddr = 0;
|
|
notes->p_paddr = 0;
|
|
notes->p_filesz = 0; /* TO BE FILLED IN */
|
|
notes->p_memsz = 0;
|
|
notes->p_flags = 0;
|
|
notes->p_align = 0;
|
|
|
|
offset += sizeof(Elf64_Phdr);
|
|
ptr += sizeof(Elf64_Phdr);
|
|
|
|
/* PT_LOAD */
|
|
load = (Elf64_Phdr *)ptr;
|
|
for (i = n = 0; i < num_segments; i++) {
|
|
load[i].p_type = PT_LOAD;
|
|
load[i].p_offset = 0; /* TO BE FILLED IN */
|
|
|
|
switch (e_machine)
|
|
{
|
|
case EM_X86_64:
|
|
nt = &vt->node_table[n];
|
|
if (i == 0) {
|
|
#ifdef X86_64
|
|
load[i].p_vaddr = __START_KERNEL_map;
|
|
load[i].p_paddr = machdep->machspec->phys_base;
|
|
#endif
|
|
load[i].p_filesz = 0;
|
|
load[i].p_memsz = load[i].p_filesz;
|
|
} else {
|
|
load[i].p_vaddr = PTOV(nt->start_paddr);
|
|
load[i].p_paddr = nt->start_paddr;
|
|
load[i].p_filesz = nt->size * PAGESIZE();
|
|
load[i].p_memsz = load[i].p_filesz;
|
|
n++;
|
|
}
|
|
load[i].p_flags = PF_R | PF_W | PF_X;
|
|
load[i].p_align = 0;
|
|
break;
|
|
|
|
case EM_386:
|
|
nt = &vt->node_table[n++];
|
|
load[i].p_vaddr = 0;
|
|
load[i].p_paddr = nt->start_paddr;
|
|
load[i].p_filesz = nt->size * PAGESIZE();
|
|
load[i].p_memsz = load[i].p_filesz;
|
|
load[i].p_flags = PF_R | PF_W | PF_X;
|
|
load[i].p_align = (type == NETDUMP_ELF64) ? PAGESIZE() : 0;
|
|
break;
|
|
|
|
case EM_IA_64:
|
|
nt = &vt->node_table[n];
|
|
if (i == 0) {
|
|
#ifdef IA64
|
|
load[i].p_vaddr = machdep->machspec->kernel_start;
|
|
load[i].p_paddr = machdep->machspec->phys_start;
|
|
#endif
|
|
load[i].p_filesz = 0;
|
|
load[i].p_memsz = load[i].p_filesz;
|
|
} else {
|
|
load[i].p_vaddr = PTOV(nt->start_paddr);
|
|
load[i].p_paddr = nt->start_paddr;
|
|
load[i].p_filesz = nt->size * PAGESIZE();
|
|
load[i].p_memsz = load[i].p_filesz;
|
|
n++;
|
|
}
|
|
load[i].p_flags = PF_R | PF_W | PF_X;
|
|
load[i].p_align = (type == NETDUMP_ELF64) ? PAGESIZE() : 0;
|
|
break;
|
|
|
|
case EM_PPC64:
|
|
nt = &vt->node_table[n++];
|
|
load[i].p_vaddr = PTOV(nt->start_paddr);
|
|
load[i].p_paddr = nt->start_paddr;
|
|
load[i].p_filesz = nt->size * PAGESIZE();
|
|
load[i].p_memsz = load[i].p_filesz;
|
|
load[i].p_flags = PF_R | PF_W | PF_X;
|
|
load[i].p_align = (type == NETDUMP_ELF64) ? PAGESIZE() : 0;
|
|
break;
|
|
|
|
case EM_AARCH64:
|
|
nt = &vt->node_table[n++];
|
|
load[i].p_vaddr = PTOV(nt->start_paddr);
|
|
load[i].p_paddr = nt->start_paddr;
|
|
load[i].p_filesz = nt->size * PAGESIZE();
|
|
load[i].p_memsz = load[i].p_filesz;
|
|
load[i].p_flags = PF_R | PF_W | PF_X;
|
|
load[i].p_align = (type == NETDUMP_ELF64) ? PAGESIZE() : 0;
|
|
break;
|
|
}
|
|
|
|
// l_offset += load[i].p_filesz;
|
|
offset += sizeof(Elf64_Phdr);
|
|
ptr += sizeof(Elf64_Phdr);
|
|
}
|
|
notes->p_offset = offset;
|
|
|
|
/* NT_PRSTATUS note */
|
|
memset(&prstatus, 0, sizeof(prstatus));
|
|
len = dump_elf_note(ptr, NT_PRSTATUS, "CORE",
|
|
(char *)&prstatus, prstatus_len);
|
|
offset += len;
|
|
ptr += len;
|
|
notes->p_filesz += len;
|
|
|
|
/* NT_PRPSINFO note */
|
|
memset(&prpsinfo, 0, sizeof(struct elf_prpsinfo_64));
|
|
prpsinfo.pr_state = 0;
|
|
prpsinfo.pr_sname = 'R';
|
|
prpsinfo.pr_zomb = 0;
|
|
strcpy(prpsinfo.pr_fname, "vmlinux");
|
|
|
|
len = dump_elf_note(ptr, NT_PRPSINFO, "CORE",
|
|
(char *)&prpsinfo, sizeof(prpsinfo));
|
|
|
|
offset += len;
|
|
ptr += len;
|
|
notes->p_filesz += len;
|
|
|
|
/* NT_TASKSTRUCT note */
|
|
SNAP_info.task_struct = CURRENT_TASK();
|
|
#ifdef X86_64
|
|
SNAP_info.arch_data1 = kt->relocate;
|
|
SNAP_info.arch_data2 = 0;
|
|
#elif ARM64
|
|
SNAP_info.arch_data1 = machdep->machspec->kimage_voffset;
|
|
SNAP_info.arch_data2 = (machdep->machspec->VA_BITS_ACTUAL << 32) |
|
|
machdep->machspec->CONFIG_ARM64_VA_BITS;
|
|
#else
|
|
SNAP_info.arch_data1 = 0;
|
|
SNAP_info.arch_data2 = 0;
|
|
#endif
|
|
len = dump_elf_note (ptr, NT_TASKSTRUCT, "SNAP",
|
|
(char *)&SNAP_info, sizeof(struct SNAP_info));
|
|
offset += len;
|
|
ptr += len;
|
|
notes->p_filesz += len;
|
|
|
|
if (type == NETDUMP_ELF64)
|
|
offset = roundup (offset, PAGESIZE());
|
|
|
|
l_offset = offset;
|
|
for (i = 0; i < num_segments; i++) {
|
|
load[i].p_offset = l_offset;
|
|
l_offset += load[i].p_filesz;
|
|
}
|
|
data_offset = offset;
|
|
|
|
while (offset > 0) {
|
|
len = write(fd, buffer + (data_offset - offset), offset);
|
|
if (len < 0) {
|
|
perror(filename);
|
|
FREEBUF(buffer);
|
|
return NULL;
|
|
}
|
|
|
|
offset -= len;
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
struct ram_segments {
|
|
physaddr_t start;
|
|
physaddr_t end;
|
|
};
|
|
|
|
static struct ram_segments *ram_segments = NULL;
|
|
static int nr_segments = 0;
|
|
|
|
static void
|
|
init_ram_segments(void)
|
|
{
|
|
int i, errflag;
|
|
FILE *iomem;
|
|
char buf[BUFSIZE], *p1, *p2;
|
|
physaddr_t start, end;
|
|
|
|
if ((iomem = fopen("/proc/iomem", "r")) == NULL)
|
|
goto fail_iomem;
|
|
|
|
while (fgets(buf, BUFSIZE, iomem)) {
|
|
if (strstr(buf, "System RAM")) {
|
|
console(buf);
|
|
nr_segments++;
|
|
}
|
|
}
|
|
if (!nr_segments)
|
|
goto fail_iomem;
|
|
|
|
ram_segments = (struct ram_segments *)
|
|
GETBUF(sizeof(struct ram_segments) * nr_segments);
|
|
|
|
rewind(iomem);
|
|
i = 0;
|
|
while (fgets(buf, BUFSIZE, iomem)) {
|
|
if (strstr(buf, "System RAM")) {
|
|
if (!(p1 = strstr(buf, ":")))
|
|
goto fail_iomem;
|
|
*p1 = NULLCHAR;
|
|
clean_line(buf);
|
|
if (strstr(buf, " "))
|
|
goto fail_iomem;
|
|
p1 = buf;
|
|
if (!(p2 = strstr(buf, "-")))
|
|
goto fail_iomem;
|
|
*p2 = NULLCHAR;
|
|
p2++;
|
|
errflag = 0;
|
|
start = htoll(p1, RETURN_ON_ERROR|QUIET, &errflag);
|
|
end = htoll(p2, RETURN_ON_ERROR|QUIET, &errflag);
|
|
if (errflag)
|
|
goto fail_iomem;
|
|
ram_segments[i].start = PHYSPAGEBASE(start);
|
|
if (PAGEOFFSET(start))
|
|
ram_segments[i].start += PAGESIZE();
|
|
ram_segments[i].end = PHYSPAGEBASE(end);
|
|
if (PAGEOFFSET(end) == (PAGESIZE()-1))
|
|
ram_segments[i].end += PAGESIZE();
|
|
console("ram_segments[%d]: %016llx %016llx [%s-%s]\n", i,
|
|
(ulonglong)ram_segments[i].start,
|
|
(ulonglong)ram_segments[i].end, p1, p2);
|
|
i++;
|
|
}
|
|
}
|
|
|
|
fclose(iomem);
|
|
return;
|
|
|
|
fail_iomem:
|
|
fclose(iomem);
|
|
nr_segments = 0;
|
|
if (ram_segments)
|
|
FREEBUF(ram_segments);
|
|
|
|
return;
|
|
}
|
|
|
|
static int
|
|
verify_paddr(physaddr_t paddr)
|
|
{
|
|
int i, ok;
|
|
|
|
if (!machdep->verify_paddr(paddr))
|
|
return FALSE;
|
|
|
|
if (!nr_segments)
|
|
return TRUE;
|
|
|
|
for (i = ok = 0; i < nr_segments; i++) {
|
|
if ((paddr >= ram_segments[i].start) &&
|
|
(paddr < ram_segments[i].end)) {
|
|
ok++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Pre-2.6.13 x86_64 /proc/iomem was restricted to 4GB,
|
|
* so just accept it.
|
|
*/
|
|
if ((paddr >= 0x100000000ULL) &&
|
|
machine_type("X86_64") &&
|
|
(THIS_KERNEL_VERSION < LINUX(2,6,13)))
|
|
ok++;
|
|
|
|
if (!ok) {
|
|
if (CRASHDEBUG(1))
|
|
console("reject: %llx\n", (ulonglong)paddr);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Borrowed from makedumpfile, prints a percentage-done value
|
|
* once per second.
|
|
*/
|
|
static int
|
|
print_progress(const char *filename, ulong current)
|
|
{
|
|
int n, progress;
|
|
time_t tm;
|
|
struct node_table *nt;
|
|
static time_t last_time = 0;
|
|
static ulong total_pages = 0;
|
|
static ulong written_pages = 0;
|
|
|
|
if (!total_pages) {
|
|
for (n = 0; n < vt->numnodes; n++) {
|
|
nt = &vt->node_table[n];
|
|
total_pages += nt->size;
|
|
}
|
|
}
|
|
|
|
if (received_SIGINT()) {
|
|
fprintf(stderr, "\n\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if (++written_pages < total_pages) {
|
|
tm = time(NULL);
|
|
if (tm - last_time < 1)
|
|
return TRUE;
|
|
last_time = tm;
|
|
progress = written_pages * 100 / total_pages;
|
|
} else
|
|
progress = 100;
|
|
|
|
fprintf(stderr, "\r%s: [%2d%%] ", filename, progress);
|
|
|
|
return TRUE;
|
|
}
|