/* ldump2mcdump.c COPYRIGHT FUJITSU LIMITED 2017 */ #include "../include/defs.h" /* From the crash source top-level directory */ #include #include #include 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(abfd, scn, cpsize); if (!ok) { bfd_perror("bfd_set_section_size"); return; } ok = bfd_set_section_flags(abfd, 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(abfd, scn, cpsize); if (!ok) { bfd_perror("bfd_set_section_size"); return; } ok = bfd_set_section_flags(abfd, 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(abfd, scn, cpsize); if (!ok) { bfd_perror("bfd_set_section_size"); return; } ok = bfd_set_section_flags(abfd, 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(abfd, scn, mem_size); if (!ok) { bfd_perror("bfd_set_section_size"); return; } ok = bfd_set_section_flags(abfd, 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(abfd, scn, mem_chunks->chunks[i].size); if (!ok) { bfd_perror("bfd_set_section_size"); return; } ok = bfd_set_section_flags(abfd, 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 */ " [-o ]", /* 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 };