485 lines
12 KiB
C
485 lines
12 KiB
C
/* ldump2mcdump.c COPYRIGHT FUJITSU LIMITED 2017 */
|
|
#include "../include/defs.h" /* From the crash source top-level directory */
|
|
#include <bfd.h>
|
|
#include <pwd.h>
|
|
#include <arch-ldump2mcdump.h>
|
|
|
|
void ldump2mcdump_init(void); /* constructor function */
|
|
void ldump2mcdump_fini(void); /* destructor function (optional) */
|
|
|
|
void cmd_ldump2mcdump(void); /* Declare the commands and their help data. */
|
|
char *help_ldump2mcdump[];
|
|
|
|
static struct command_table_entry command_table[] = {
|
|
{ "ldump2mcdump", cmd_ldump2mcdump, help_ldump2mcdump, 0}, /* One or more commands, */
|
|
{ NULL }, /* terminated by NULL, */
|
|
};
|
|
|
|
|
|
void __attribute__((constructor))
|
|
ldump2mcdump_init(void) /* Register the command set. */
|
|
{
|
|
register_extension(command_table);
|
|
}
|
|
|
|
/*
|
|
* This function is called if the shared object is unloaded.
|
|
* If desired, perform any cleanups here.
|
|
*/
|
|
void __attribute__((destructor))
|
|
ldump2mcdump_fini(void) { }
|
|
|
|
struct ihk_dump_page {
|
|
unsigned long start;
|
|
unsigned long map_count;
|
|
unsigned long map[0];
|
|
};
|
|
|
|
struct ihk_dump_page_set {
|
|
unsigned int completion_flag;
|
|
unsigned int count;
|
|
unsigned long page_size;
|
|
unsigned long phy_page;
|
|
};
|
|
|
|
struct dump_mem_chunk {
|
|
unsigned long addr;
|
|
unsigned long size;
|
|
};
|
|
|
|
typedef struct dump_mem_chunks_s {
|
|
int nr_chunks;
|
|
unsigned long kernel_base;
|
|
/* memstart_addr in aarch64 */
|
|
unsigned long phys_start;
|
|
struct dump_mem_chunk chunks[];
|
|
} dump_mem_chunks_t;
|
|
|
|
#define PATH_MAX 4096
|
|
#define DUMP_MEM_SYMBOL "dump_page_set_addr"
|
|
#define BOOTSTRAP_MEM_SYMBOL "dump_bootstrap_mem_start"
|
|
#define MCDUMP_DEFAULT_FILENAME "mcdump"
|
|
#define LARGE_PAGE_SIZE (1UL << LARGE_PAGE_SHIFT)
|
|
#define LARGE_PAGE_MASK (~((unsigned long)LARGE_PAGE_SIZE - 1))
|
|
|
|
#define PHYSMEM_NAME_SIZE 32
|
|
|
|
void cmd_ldump2mcdump(void)
|
|
{
|
|
static char path[PATH_MAX];
|
|
static char hname[HOST_NAME_MAX+1];
|
|
bfd *abfd = NULL;
|
|
char *fname;
|
|
bfd_boolean ok;
|
|
asection *scn;
|
|
unsigned long phys_size, phys_offset;
|
|
int error;
|
|
size_t bsize;
|
|
void *buf = NULL;
|
|
uintptr_t addr;
|
|
size_t cpsize;
|
|
time_t t;
|
|
struct tm *tm;
|
|
char *date;
|
|
struct passwd *pw;
|
|
dump_mem_chunks_t *mem_chunks = NULL;
|
|
long mem_size;
|
|
int opt = 0;
|
|
int read_mem_ret = TRUE;
|
|
|
|
ulong symbol_dump_page_set = 0;
|
|
ulong dump_page_set_addr = 0;
|
|
ulong symbol_bootstrap_mem = 0;
|
|
ulong bootstrap_mem = 0;
|
|
struct ihk_dump_page_set dump_page_set;
|
|
ulong ihk_dump_page_addr = 0;
|
|
struct ihk_dump_page ihk_dump_page;
|
|
ulong *map_buf = NULL;
|
|
ulong map_size = 0;
|
|
int i,j,k,index,mem_num;
|
|
ulong map_start,bit_count;
|
|
char *physmem_name_buf = NULL;
|
|
char physmem_name[PHYSMEM_NAME_SIZE];
|
|
|
|
ulong read_mem_addr = 0;
|
|
|
|
if (argcnt < 2) {
|
|
perror("argument error");
|
|
return;
|
|
}
|
|
|
|
strcpy(path,MCDUMP_DEFAULT_FILENAME);
|
|
|
|
while ((opt = getopt(argcnt, args, "o:")) != -1) {
|
|
switch (opt) {
|
|
case 'o': /* '-o' */
|
|
strcpy(path,optarg);
|
|
break;
|
|
default: /* '?' */
|
|
fprintf(stderr, "ldump2mcdump os_index [-o file_name]\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
fname = path;
|
|
symbol_dump_page_set = symbol_value(DUMP_MEM_SYMBOL);
|
|
readmem(symbol_dump_page_set,KVADDR,&dump_page_set_addr,sizeof(dump_page_set_addr),"",FAULT_ON_ERROR);
|
|
readmem(dump_page_set_addr,KVADDR,&dump_page_set,sizeof(dump_page_set),"",FAULT_ON_ERROR);
|
|
|
|
// DUMP_QUERY_NUM_MEM_AREAS
|
|
ihk_dump_page_addr = PTOV(dump_page_set.phy_page);
|
|
for (i = 0, mem_num = 0; i < dump_page_set.count; i++) {
|
|
|
|
readmem(ihk_dump_page_addr,KVADDR,&ihk_dump_page,sizeof(ihk_dump_page),"",FAULT_ON_ERROR);
|
|
map_size = sizeof(unsigned long)*ihk_dump_page.map_count;
|
|
map_buf = malloc(map_size);
|
|
if (map_buf != NULL) {
|
|
memset(map_buf,0x00,map_size);
|
|
readmem((ihk_dump_page_addr+sizeof(struct ihk_dump_page)),KVADDR,map_buf,map_size,"",FAULT_ON_ERROR);
|
|
|
|
for (j = 0, bit_count = 0; j < ihk_dump_page.map_count; j++) {
|
|
for ( k = 0; k < 64; k++) {
|
|
if (((ulong)*(map_buf+j) >> k) & 0x1) {
|
|
bit_count++;
|
|
} else {
|
|
if (bit_count) {
|
|
mem_num++;
|
|
bit_count = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bit_count) {
|
|
mem_num++;
|
|
}
|
|
free(map_buf);
|
|
} else {
|
|
perror("allocating mem buffer: ");
|
|
return;
|
|
}
|
|
|
|
ihk_dump_page_addr += (sizeof(struct ihk_dump_page)+(sizeof(unsigned long)*ihk_dump_page.map_count));
|
|
}
|
|
mem_size = (sizeof(dump_mem_chunks_t) + (sizeof(struct dump_mem_chunk) * mem_num));
|
|
|
|
// DUMP_QUERY_MEM_AREAS
|
|
mem_chunks = malloc(mem_size);
|
|
if (mem_chunks != NULL) {
|
|
memset(mem_chunks, 0, mem_size);
|
|
ihk_dump_page_addr = PTOV(dump_page_set.phy_page);
|
|
|
|
for (i = 0, index = 0; i < dump_page_set.count; i++) {
|
|
|
|
readmem(ihk_dump_page_addr,KVADDR,&ihk_dump_page,sizeof(ihk_dump_page),"",FAULT_ON_ERROR);
|
|
map_size = sizeof(unsigned long)*ihk_dump_page.map_count;
|
|
map_buf = malloc(map_size);
|
|
if (map_buf != NULL) {
|
|
memset(map_buf,0x00,map_size);
|
|
readmem((ihk_dump_page_addr+sizeof(struct ihk_dump_page)),KVADDR,map_buf,map_size,"",FAULT_ON_ERROR);
|
|
|
|
for (j = 0, bit_count = 0; j < ihk_dump_page.map_count; j++) {
|
|
for (k = 0; k < 64; k++) {
|
|
if (((ulong)*(map_buf+j) >> k) & 0x1) {
|
|
if (!bit_count) {
|
|
map_start = (unsigned long)(ihk_dump_page.start + ((unsigned long)j << (PAGE_SHIFT+6)));
|
|
map_start = map_start + ((unsigned long)k << PAGE_SHIFT);
|
|
}
|
|
bit_count++;
|
|
} else {
|
|
if (bit_count) {
|
|
mem_chunks->chunks[index].addr = map_start;
|
|
mem_chunks->chunks[index].size = (bit_count << PAGE_SHIFT);
|
|
index++;
|
|
bit_count = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bit_count) {
|
|
mem_chunks->chunks[index].addr = map_start;
|
|
mem_chunks->chunks[index].size = (bit_count << PAGE_SHIFT);
|
|
index++;
|
|
}
|
|
|
|
ihk_dump_page_addr += (sizeof(struct ihk_dump_page)+(sizeof(unsigned long)*ihk_dump_page.map_count));
|
|
free(map_buf);
|
|
} else {
|
|
perror("allocating mem buffer: ");
|
|
return;
|
|
}
|
|
|
|
}
|
|
mem_chunks->nr_chunks = index;
|
|
|
|
symbol_bootstrap_mem = symbol_value(BOOTSTRAP_MEM_SYMBOL);
|
|
|
|
readmem(symbol_bootstrap_mem,KVADDR,&bootstrap_mem,sizeof(bootstrap_mem),"",FAULT_ON_ERROR);
|
|
|
|
/* See load_file() for the calculation below */
|
|
mem_chunks->kernel_base =
|
|
(bootstrap_mem + LARGE_PAGE_SIZE * 2 - 1) & LARGE_PAGE_MASK;
|
|
} else {
|
|
perror("allocating mem buffer: ");
|
|
return;
|
|
}
|
|
|
|
// DUMP_READ
|
|
phys_size = 0;
|
|
|
|
// fprintf(fp,"%s: nr chunks: %d\n", __FUNCTION__, mem_chunks->nr_chunks);
|
|
for (i = 0; i < mem_chunks->nr_chunks; ++i) {
|
|
// fprintf(fp,"%s: 0x%lx:0x%lx\n",
|
|
// __FUNCTION__,
|
|
// mem_chunks->chunks[i].addr,
|
|
// mem_chunks->chunks[i].size);
|
|
phys_size += mem_chunks->chunks[i].size;
|
|
}
|
|
|
|
bsize = 0x100000;
|
|
buf = malloc(bsize);
|
|
if (!buf) {
|
|
perror("malloc");
|
|
return;
|
|
}
|
|
|
|
bfd_init();
|
|
|
|
abfd = bfd_fopen(fname, NULL, "w", -1);
|
|
if (!abfd) {
|
|
bfd_perror("bfd_fopen");
|
|
return;
|
|
}
|
|
|
|
ok = bfd_set_format(abfd, bfd_object);
|
|
if (!ok) {
|
|
bfd_perror("bfd_set_format");
|
|
return;
|
|
}
|
|
|
|
t = time(NULL);
|
|
if (t == (time_t)-1) {
|
|
perror("time");
|
|
return;
|
|
}
|
|
|
|
tm = localtime(&t);
|
|
if (!tm) {
|
|
perror("localtime");
|
|
return;
|
|
}
|
|
|
|
date = asctime(tm);
|
|
if (date) {
|
|
cpsize = strlen(date) - 1; /* exclude trailing '\n' */
|
|
scn = bfd_make_section_anyway(abfd, "date");
|
|
if (!scn) {
|
|
bfd_perror("bfd_make_section_anyway(date)");
|
|
return;
|
|
}
|
|
|
|
ok = bfd_set_section_size(scn, cpsize);
|
|
if (!ok) {
|
|
bfd_perror("bfd_set_section_size");
|
|
return;
|
|
}
|
|
|
|
ok = bfd_set_section_flags(scn, SEC_HAS_CONTENTS);
|
|
if (!ok) {
|
|
bfd_perror("bfd_set_setction_flags");
|
|
return;
|
|
}
|
|
}
|
|
error = gethostname(hname, sizeof(hname));
|
|
if (!error) {
|
|
cpsize = strlen(hname);
|
|
scn = bfd_make_section_anyway(abfd, "hostname");
|
|
if (!scn) {
|
|
bfd_perror("bfd_make_section_anyway(hostname)");
|
|
return;
|
|
}
|
|
|
|
ok = bfd_set_section_size(scn, cpsize);
|
|
if (!ok) {
|
|
bfd_perror("bfd_set_section_size");
|
|
return;
|
|
}
|
|
|
|
ok = bfd_set_section_flags(scn, SEC_HAS_CONTENTS);
|
|
if (!ok) {
|
|
bfd_perror("bfd_set_setction_flags");
|
|
return;
|
|
}
|
|
}
|
|
pw = getpwuid(getuid());
|
|
if (pw) {
|
|
cpsize = strlen(pw->pw_name);
|
|
scn = bfd_make_section_anyway(abfd, "user");
|
|
if (!scn) {
|
|
bfd_perror("bfd_make_section_anyway(user)");
|
|
return;
|
|
}
|
|
|
|
ok = bfd_set_section_size(scn, cpsize);
|
|
if (!ok) {
|
|
bfd_perror("bfd_set_section_size");
|
|
return;
|
|
}
|
|
|
|
ok = bfd_set_section_flags(scn, SEC_HAS_CONTENTS);
|
|
if (!ok) {
|
|
bfd_perror("bfd_set_setction_flags");
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Add section for physical memory chunks information */
|
|
scn = bfd_make_section_anyway(abfd, "physchunks");
|
|
if (!scn) {
|
|
bfd_perror("bfd_make_section_anyway(physchunks)");
|
|
return;
|
|
}
|
|
|
|
ok = bfd_set_section_size(scn, mem_size);
|
|
if (!ok) {
|
|
bfd_perror("bfd_set_section_size");
|
|
return;
|
|
}
|
|
|
|
ok = bfd_set_section_flags(scn, SEC_ALLOC|SEC_HAS_CONTENTS);
|
|
if (!ok) {
|
|
bfd_perror("bfd_set_setction_flags");
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < mem_chunks->nr_chunks; ++i) {
|
|
|
|
physmem_name_buf = malloc(PHYSMEM_NAME_SIZE);
|
|
memset(physmem_name_buf,0,PHYSMEM_NAME_SIZE);
|
|
sprintf(physmem_name_buf, "physmem%d",i);
|
|
|
|
/* Physical memory contents section */
|
|
scn = bfd_make_section_anyway(abfd, physmem_name_buf);
|
|
if (!scn) {
|
|
bfd_perror("bfd_make_section_anyway(physmem)");
|
|
return;
|
|
}
|
|
|
|
ok = bfd_set_section_size(scn, mem_chunks->chunks[i].size);
|
|
|
|
if (!ok) {
|
|
bfd_perror("bfd_set_section_size");
|
|
return;
|
|
}
|
|
|
|
ok = bfd_set_section_flags(scn, SEC_ALLOC|SEC_HAS_CONTENTS);
|
|
if (!ok) {
|
|
bfd_perror("bfd_set_setction_flags");
|
|
return;
|
|
}
|
|
|
|
scn->vma = mem_chunks->chunks[i].addr;
|
|
|
|
}
|
|
|
|
scn = bfd_get_section_by_name(abfd, "date");
|
|
if (scn) {
|
|
ok = bfd_set_section_contents(abfd, scn, date, 0, scn->size);
|
|
if (!ok) {
|
|
bfd_perror("bfd_set_section_contents(date)");
|
|
return;
|
|
}
|
|
}
|
|
|
|
scn = bfd_get_section_by_name(abfd, "hostname");
|
|
if (scn) {
|
|
ok = bfd_set_section_contents(abfd, scn, hname, 0, scn->size);
|
|
if (!ok) {
|
|
bfd_perror("bfd_set_section_contents(hostname)");
|
|
return;
|
|
}
|
|
}
|
|
|
|
scn = bfd_get_section_by_name(abfd, "user");
|
|
if (scn) {
|
|
ok = bfd_set_section_contents(abfd, scn, pw->pw_name, 0, scn->size);
|
|
if (!ok) {
|
|
bfd_perror("bfd_set_section_contents(user)");
|
|
return;
|
|
}
|
|
}
|
|
|
|
scn = bfd_get_section_by_name(abfd, "physchunks");
|
|
if (scn) {
|
|
ok = bfd_set_section_contents(abfd, scn, mem_chunks, 0, mem_size);
|
|
if (!ok) {
|
|
bfd_perror("bfd_set_section_contents(physchunks)");
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < mem_chunks->nr_chunks; ++i) {
|
|
|
|
phys_offset = 0;
|
|
memset(physmem_name,0,sizeof(physmem_name));
|
|
sprintf(physmem_name, "physmem%d",i);
|
|
|
|
scn = bfd_get_section_by_name(abfd, physmem_name);
|
|
if (!scn) {
|
|
bfd_perror("err bfd_get_section_by_name(physmem_name)");
|
|
return ;
|
|
}
|
|
|
|
for (addr = mem_chunks->chunks[i].addr;
|
|
addr < (mem_chunks->chunks[i].addr + mem_chunks->chunks[i].size);
|
|
addr += cpsize) {
|
|
|
|
cpsize = (mem_chunks->chunks[i].addr + mem_chunks->chunks[i].size) - addr;
|
|
if (cpsize > bsize) {
|
|
cpsize = bsize;
|
|
}
|
|
|
|
memset(buf,0x00,cpsize);
|
|
read_mem_addr = PTOV(addr);
|
|
read_mem_ret = readmem(read_mem_addr,KVADDR,buf,cpsize,"",FAULT_ON_ERROR|RETURN_ON_ERROR);
|
|
if (read_mem_ret == TRUE) {
|
|
ok = bfd_set_section_contents(abfd, scn, buf, phys_offset, cpsize);
|
|
if (!ok) {
|
|
bfd_perror("bfd_set_section_contents(physmem)");
|
|
return;
|
|
}
|
|
|
|
phys_offset += cpsize;
|
|
} else {
|
|
fprintf(fp, "readmem error(%d)\n",read_mem_ret);
|
|
}
|
|
}
|
|
}
|
|
|
|
ok = bfd_close(abfd);
|
|
if (!ok) {
|
|
bfd_perror("bfd_close");
|
|
return;
|
|
}
|
|
|
|
free(buf);
|
|
free(mem_chunks);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
char *help_ldump2mcdump[] = {
|
|
"ldump2mcdump", /* command name */
|
|
"dump format conversion", /* short description */
|
|
"<os_index> [-o <file_name>]", /* argument synopsis, or " " if none */
|
|
" This command converts the McKernel dump file format.",
|
|
"\nEXAMPLE",
|
|
" ldump2mcdump all command arguments:\n",
|
|
" crash>ldump2mcdump 0 -o /tmp/mcdump",
|
|
NULL
|
|
};
|
|
|
|
|