Below commit includes changes except of those noted in other commit. commit 1fb344e934f87b9c124e487a21e111c7bdc977ba Author: Ken'ichi Ohmichi Date: Fri Oct 8 09:18:59 2010 +0900 [PATCH] Add ELF note section to the kdump-compressed format. change in get_pt_note_info() is included in commit below: commit 5c814282829eeb2a8f83c9067ee48298c7bf14be Author: Ken'ichi Ohmichi Date: Wed Sep 7 16:32:48 2011 +0900 [PATCH] Cleanup: Add elf_info.c for reducing makedumpfile.c change in dump_dmesg() is included in commit below: commit 8bb3436b1c13f02796d417b2f76af4ed642e51c9 Author: Ken'ichi Ohmichi Date: Thu Nov 11 12:54:40 2010 +0900 [PATCH] Fix the size of "log_end" which has been changed since linux-2.6.25. Below commit includes changes related to Makefile, makedumpfile.h, s390x.c commit 0f7076d6f179f9bbd446c3e21eb63c52369a0f0d Author: Mahesh Salgaonkar Date: Mon Nov 15 12:39:17 2010 +0900 [PATCH] Add support for s390x crashdump analysis. Index: kexec-tools-2.0.0/makedumpfile-1.3.5/Makefile =================================================================== --- kexec-tools-2.0.0.orig/makedumpfile-1.3.5/Makefile +++ kexec-tools-2.0.0/makedumpfile-1.3.5/Makefile @@ -23,8 +23,8 @@ CFLAGS_ARCH += -m64 endif SRC = makedumpfile.c makedumpfile.h diskdump_mod.h -SRC_ARCH = x86.c x86_64.c ia64.c ppc64.c -OBJ_ARCH = x86.o x86_64.o ia64.o ppc64.o +SRC_ARCH = x86.c x86_64.c ia64.c ppc64.c s390x.c +OBJ_ARCH = x86.o x86_64.o ia64.o ppc64.o s390x.o all: makedumpfile Index: kexec-tools-2.0.0/makedumpfile-1.3.5/diskdump_mod.h =================================================================== --- kexec-tools-2.0.0.orig/makedumpfile-1.3.5/diskdump_mod.h +++ kexec-tools-2.0.0/makedumpfile-1.3.5/diskdump_mod.h @@ -73,6 +73,8 @@ struct kdump_sub_header { unsigned long end_pfn; /* header_version 2 and later */ off_t offset_vmcoreinfo;/* header_version 3 and later */ unsigned long size_vmcoreinfo; /* header_version 3 and later */ + off_t offset_note; /* header_version 4 and later */ + unsigned long size_note; /* header_version 4 and later */ }; /* page flags */ Index: kexec-tools-2.0.0/makedumpfile-1.3.5/makedumpfile.c =================================================================== --- kexec-tools-2.0.0.orig/makedumpfile-1.3.5/makedumpfile.c +++ kexec-tools-2.0.0/makedumpfile-1.3.5/makedumpfile.c @@ -902,6 +902,11 @@ get_kdump_compressed_header_info(char *f info->offset_vmcoreinfo = kh.offset_vmcoreinfo; info->size_vmcoreinfo = kh.size_vmcoreinfo; } + if (dh.header_version >= 4) { + /* A dumpfile contains ELF note section. */ + info->offset_note = kh.offset_note; + info->size_note = kh.size_note; + } return TRUE; error: free(info->dh_memory); @@ -1262,8 +1267,6 @@ int get_elf_info(void) { int i, j, phnum, num_load, elf_format; - off_t offset_note; - unsigned long size_note; Elf64_Phdr phdr; /* @@ -1292,15 +1295,15 @@ get_elf_info(void) strerror(errno)); return FALSE; } - offset_note = 0; - size_note = 0; + info->offset_note = 0; + info->size_note = 0; for (i = 0, j = 0; i < phnum; i++) { if (!get_elf_phdr_memory(i, &phdr)) return FALSE; if (phdr.p_type == PT_NOTE) { - offset_note = phdr.p_offset; - size_note = phdr.p_filesz; + info->offset_note = phdr.p_offset; + info->size_note = phdr.p_filesz; } if (phdr.p_type != PT_LOAD) continue; @@ -1318,11 +1321,11 @@ get_elf_info(void) return FALSE; j++; } - if (offset_note == 0 || size_note == 0) { + if (info->offset_note == 0 || info->size_note == 0) { ERRMSG("Can't find PT_NOTE Phdr.\n"); return FALSE; } - if (!get_pt_note_info(offset_note, size_note)) { + if (!get_pt_note_info(info->offset_note, info->size_note)) { ERRMSG("Can't get PT_NOTE information.\n"); return FALSE; } @@ -2895,6 +2898,12 @@ get_pt_note_info(off_t off_note, unsigne return FALSE; } n_type = note_type(note); + + if (n_type == NT_PRSTATUS) { + info->nr_cpus++; + offset += offset_next_note(note); + continue; + } offset_desc = offset + offset_note_desc(note); size_desc = note_descsz(note); @@ -4534,8 +4543,15 @@ dump_dmesg() { int log_buf_len, length_log, length_oldlog, ret = FALSE; unsigned long log_buf, log_end, index; + unsigned long log_end_2_6_24; + unsigned log_end_2_6_25; char *log_buffer = NULL; + /* + * log_end has been changed to "unsigned" since linux-2.6.25. + * 2.6.24 or former: static unsigned long log_end; + * 2.6.25 or later : static unsigned log_end; + */ if (!open_files_for_creating_dumpfile()) return FALSE; @@ -4556,9 +4572,20 @@ dump_dmesg() ERRMSG("Can't get log_buf.\n"); return FALSE; } - if (!readmem(VADDR, SYMBOL(log_end), &log_end, sizeof(log_end))) { - ERRMSG("Can't to get log_end.\n"); - return FALSE; + if (info->kernel_version >= KERNEL_VERSION(2, 6, 25)) { + if (!readmem(VADDR, SYMBOL(log_end), &log_end_2_6_25, + sizeof(log_end_2_6_25))) { + ERRMSG("Can't to get log_end.\n"); + return FALSE; + } + log_end = log_end_2_6_25; + } else { + if (!readmem(VADDR, SYMBOL(log_end), &log_end_2_6_24, + sizeof(log_end_2_6_24))) { + ERRMSG("Can't to get log_end.\n"); + return FALSE; + } + log_end = log_end_2_6_24; } if (!readmem(VADDR, SYMBOL(log_buf_len), &log_buf_len, sizeof(log_buf_len))) { @@ -5485,12 +5512,12 @@ write_kdump_header(void) * Write common header */ strcpy(dh->signature, KDUMP_SIGNATURE); - dh->header_version = 3; + dh->header_version = 4; dh->block_size = info->page_size; - dh->sub_hdr_size = sizeof(kh) + info->size_vmcoreinfo; + dh->sub_hdr_size = sizeof(kh) + info->size_note; dh->sub_hdr_size = divideup(dh->sub_hdr_size, dh->block_size); dh->max_mapnr = info->max_mapnr; - dh->nr_cpus = 1; + dh->nr_cpus = info->nr_cpus; dh->bitmap_blocks = divideup(info->len_bitmap, dh->block_size); memcpy(&dh->timestamp, &info->timestamp, sizeof(dh->timestamp)); memcpy(&dh->utsname, &info->system_utsname, sizeof(dh->utsname)); @@ -5511,35 +5538,47 @@ write_kdump_header(void) kh.start_pfn = info->split_start_pfn; kh.end_pfn = info->split_end_pfn; } - if (info->offset_vmcoreinfo && info->size_vmcoreinfo) { + if (info->offset_note && info->size_note) { /* - * Write vmcoreinfo data + * Write ELF note section */ - kh.offset_vmcoreinfo + kh.offset_note = DISKDUMP_HEADER_BLOCKS * dh->block_size + sizeof(kh); - kh.size_vmcoreinfo = info->size_vmcoreinfo; + kh.size_note = info->size_note; - buf = malloc(info->size_vmcoreinfo); + buf = malloc(info->size_note); if (buf == NULL) { - ERRMSG("Can't allocate memory for vmcoreinfo. %s\n", + ERRMSG("Can't allocate memory for ELF note section. %s\n", strerror(errno)); return FALSE; } - if (lseek(info->fd_memory, info->offset_vmcoreinfo, SEEK_SET) - < 0) { + if (lseek(info->fd_memory, info->offset_note, SEEK_SET) < 0) { ERRMSG("Can't seek the dump memory(%s). %s\n", info->name_memory, strerror(errno)); goto out; } - if (read(info->fd_memory, buf, info->size_vmcoreinfo) - != info->size_vmcoreinfo) { + if (read(info->fd_memory, buf, info->size_note) + != info->size_note) { ERRMSG("Can't read the dump memory(%s). %s\n", info->name_memory, strerror(errno)); goto out; } - if (!write_buffer(info->fd_dumpfile, kh.offset_vmcoreinfo, buf, - kh.size_vmcoreinfo, info->name_dumpfile)) + if (!write_buffer(info->fd_dumpfile, kh.offset_note, buf, + kh.size_note, info->name_dumpfile)) goto out; + + if (info->offset_vmcoreinfo && info->size_vmcoreinfo) { + /* + * Set vmcoreinfo data + * + * NOTE: ELF note section contains vmcoreinfo data, and + * kh.offset_vmcoreinfo points the vmcoreinfo data. + */ + kh.offset_vmcoreinfo + = info->offset_vmcoreinfo - info->offset_note + + kh.offset_note; + kh.size_vmcoreinfo = info->size_vmcoreinfo; + } } if (!write_buffer(info->fd_dumpfile, dh->block_size, &kh, size, info->name_dumpfile)) Index: kexec-tools-2.0.0/makedumpfile-1.3.5/makedumpfile.h =================================================================== --- kexec-tools-2.0.0.orig/makedumpfile-1.3.5/makedumpfile.h +++ kexec-tools-2.0.0/makedumpfile-1.3.5/makedumpfile.h @@ -597,6 +597,45 @@ do { \ #define _MAX_PHYSMEM_BITS (44) #endif +#ifdef __s390x__ +#define __PAGE_OFFSET (info->page_size - 1) +#define KERNELBASE (0) +#define KVBASE (SYMBOL(_stext)) +#define _SECTION_SIZE_BITS (28) +#define _MAX_PHYSMEM_BITS (42) + +/* Bits in the segment/region table address-space-control-element */ +#define _ASCE_TYPE_MASK 0x0c +#define _ASCE_TABLE_LENGTH 0x03 /* region table length */ + +#define TABLE_LEVEL(x) (((x) & _ASCE_TYPE_MASK) >> 2) +#define TABLE_LENGTH(x) ((x) & _ASCE_TABLE_LENGTH) + +/* Bits in the region table entry */ +#define _REGION_ENTRY_ORIGIN ~0xfffUL /* region table origin*/ +#define _REGION_ENTRY_TYPE_MASK 0x0c /* region table type mask */ +#define _REGION_ENTRY_INVALID 0x20 /* invalid region table entry */ +#define _REGION_ENTRY_LENGTH 0x03 /* region table length */ +#define _REGION_OFFSET_MASK 0x7ffUL /* region/segment table offset mask */ + +#define RSG_TABLE_LEVEL(x) (((x) & _REGION_ENTRY_TYPE_MASK) >> 2) +#define RSG_TABLE_LENGTH(x) ((x) & _REGION_ENTRY_LENGTH) + +/* Bits in the segment table entry */ +#define _SEGMENT_ENTRY_ORIGIN ~0x7ffUL +#define _SEGMENT_ENTRY_LARGE 0x400 +#define _SEGMENT_PAGE_SHIFT 31 +#define _SEGMENT_INDEX_SHIFT 20 + +/* Hardware bits in the page table entry */ +#define _PAGE_CO 0x100 /* HW Change-bit override */ +#define _PAGE_ZERO 0x800 /* Bit pos 52 must conatin zero */ +#define _PAGE_INVALID 0x400 /* HW invalid bit */ +#define _PAGE_INDEX_SHIFT 12 +#define _PAGE_OFFSET_MASK 0xffUL /* page table offset mask */ + +#endif /* __s390x__ */ + #ifdef __ia64__ /* ia64 */ #define REGION_SHIFT (61) @@ -685,6 +724,15 @@ unsigned long long vaddr_to_paddr_ppc64( #define vaddr_to_paddr(X) vaddr_to_paddr_ppc64(X) #endif /* powerpc */ +#ifdef __s390x__ /* s390x */ +int get_machdep_info_s390x(void); +unsigned long long vaddr_to_paddr_s390x(unsigned long vaddr); +#define get_phys_base() TRUE +#define get_machdep_info() get_machdep_info_s390x() +#define get_versiondep_info() TRUE +#define vaddr_to_paddr(X) vaddr_to_paddr_s390x(X) +#endif /* s390x */ + #ifdef __ia64__ /* ia64 */ int get_phys_base_ia64(void); int get_machdep_info_ia64(void); @@ -793,6 +841,7 @@ struct DumpInfo { unsigned long vaddr_for_vtop; /* virtual address for debugging */ long page_size; /* size of page */ long page_shift; + int nr_cpus; /* number of cpu */ unsigned long long max_mapnr; /* number of page descriptor */ unsigned long page_offset; unsigned long section_size_bits; @@ -876,6 +925,12 @@ struct DumpInfo { unsigned long size_vmcoreinfo_xen; /* + * ELF NOTE section in dump memory image info: + */ + off_t offset_note; + unsigned long size_note; + + /* * for Xen extraction */ off_t offset_xen_crash_info; @@ -1229,3 +1284,7 @@ int get_xen_info_ia64(void); #define get_xen_info_arch(X) FALSE #endif /* powerpc */ +#ifdef __s390x__ /* s390x */ +#define kvtop_xen(X) FALSE +#define get_xen_info_arch(X) FALSE +#endif /* s390x */ Index: kexec-tools-2.0.0/makedumpfile-1.3.5/s390x.c =================================================================== --- /dev/null +++ kexec-tools-2.0.0/makedumpfile-1.3.5/s390x.c @@ -0,0 +1,280 @@ +/* + * s390x.c + * + * Created by: Michael Holzheu (holzheu@de.ibm.com) + * Copyright IBM Corp. 2010 + * + * 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 (version 2 of the License). + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifdef __s390x__ + +#include "makedumpfile.h" + +#define TABLE_SIZE 4096 + +/* + * Bits in the virtual address + * + * |<----- RX ---------->| + * | RFX | RSX | RTX | SX | PX | BX | + * 0 11 22 33 44 52 63 + * + * RX: Region Index + * RFX: Region first index + * RSX: Region second index + * RTX: Region third index + * SX: Segment index + * PX: Page index + * BX: Byte index + * + * RX part of vaddr is divided into three fields RFX, RSX and RTX each of + * 11 bit in size + */ +#define _REGION_INDEX_SHIFT 11 +#define _PAGE_INDEX_MASK 0xff000UL /* page index (PX) mask */ +#define _BYTE_INDEX_MASK 0x00fffUL /* Byte index (BX) mask */ +#define _PAGE_BYTE_INDEX_MASK (_PAGE_INDEX_MASK | _BYTE_INDEX_MASK) + +/* Region/segment table index */ +#define rsg_index(x, y) \ + (((x) >> ((_REGION_INDEX_SHIFT * y) + _SEGMENT_INDEX_SHIFT)) \ + & _REGION_OFFSET_MASK) +/* Page table index */ +#define pte_index(x) (((x) >> _PAGE_INDEX_SHIFT) & _PAGE_OFFSET_MASK) + +#define rsg_offset(x, y) (rsg_index( x, y) * sizeof(unsigned long)) +#define pte_offset(x) (pte_index(x) * sizeof(unsigned long)) + +int +get_machdep_info_s390x(void) +{ + unsigned long vmlist, vmalloc_start; + + info->section_size_bits = _SECTION_SIZE_BITS; + info->max_physmem_bits = _MAX_PHYSMEM_BITS; + info->page_offset = __PAGE_OFFSET; + + if (SYMBOL(_stext) == NOT_FOUND_SYMBOL) { + ERRMSG("Can't get the symbol of _stext.\n"); + return FALSE; + } + info->kernel_start = SYMBOL(_stext); + DEBUG_MSG("kernel_start : %lx\n", info->kernel_start); + + /* + * For the compatibility, makedumpfile should run without the symbol + * vmlist and the offset of vm_struct.addr if they are not necessary. + */ + if ((SYMBOL(vmlist) == NOT_FOUND_SYMBOL) + || (OFFSET(vm_struct.addr) == NOT_FOUND_STRUCTURE)) { + return TRUE; + } + if (!readmem(VADDR, SYMBOL(vmlist), &vmlist, sizeof(vmlist))) { + ERRMSG("Can't get vmlist.\n"); + return FALSE; + } + if (!readmem(VADDR, vmlist + OFFSET(vm_struct.addr), &vmalloc_start, + sizeof(vmalloc_start))) { + ERRMSG("Can't get vmalloc_start.\n"); + return FALSE; + } + info->vmalloc_start = vmalloc_start; + DEBUG_MSG("vmalloc_start: %lx\n", vmalloc_start); + + return TRUE; +} + +static int +is_vmalloc_addr_s390x(unsigned long vaddr) +{ + return (info->vmalloc_start && vaddr >= info->vmalloc_start); +} + +static int +rsg_table_entry_bad(unsigned long entry, int level) +{ + unsigned long mask = ~_REGION_ENTRY_INVALID + & ~_REGION_ENTRY_TYPE_MASK + & ~_REGION_ENTRY_LENGTH; + + if (level) + mask &= ~_REGION_ENTRY_ORIGIN; + else + mask &= ~_SEGMENT_ENTRY_ORIGIN; + + return (entry & mask) != 0; +} + +/* Region or segment table traversal function */ +static unsigned long +_kl_rsg_table_deref_s390x(unsigned long vaddr, unsigned long table, + int len, int level) +{ + unsigned long offset, entry; + + offset = rsg_offset(vaddr, level); + + /* check if offset is over the table limit. */ + if (offset >= ((len + 1) * TABLE_SIZE)) { + ERRMSG("offset is over the table limit.\n"); + return 0; + } + + if (!readmem(VADDR, table + offset, &entry, sizeof(entry))) { + if (level) + ERRMSG("Can't read region table %d entry\n", level); + else + ERRMSG("Can't read segment table entry\n"); + return 0; + } + /* + * Check if the segment table entry could be read and doesn't have + * any of the reserved bits set. + */ + if (rsg_table_entry_bad(entry, level)) { + ERRMSG("Bad region/segment table entry.\n"); + return 0; + } + /* + * Check if the region/segment table entry is with valid + * level and not invalid. + */ + if ((RSG_TABLE_LEVEL(entry) != level) + && (entry & _REGION_ENTRY_INVALID)) { + ERRMSG("Invalid region/segment table level or entry.\n"); + return 0; + } + + return entry; +} + +/* Page table traversal function */ +static ulong _kl_pg_table_deref_s390x(unsigned long vaddr, unsigned long table) +{ + unsigned long offset, entry; + + offset = pte_offset(vaddr); + readmem(VADDR, table + offset, &entry, sizeof(entry)); + /* + * Check if the page table entry could be read and doesn't have + * any of the reserved bits set. + * Check if the page table entry has the invalid bit set. + */ + if (entry & (_PAGE_CO | _PAGE_ZERO | _PAGE_INVALID)) { + ERRMSG("Invalid page table entry.\n"); + return 0; + } + + return entry; +} + +/* vtop_s390x() - translate virtual address to physical + * @vaddr: virtual address to translate + * + * Function converts the @vaddr into physical address using page tables. + * + * Return: + * Physical address or NOT_PADDR if translation fails. + */ +static unsigned long long +vtop_s390x(unsigned long vaddr) +{ + unsigned long long paddr = NOT_PADDR; + unsigned long table, entry; + int level, len; + + if (SYMBOL(swapper_pg_dir) == NOT_FOUND_SYMBOL) { + ERRMSG("Can't get the symbol of swapper_pg_dir.\n"); + return NOT_PADDR; + } + table = SYMBOL(swapper_pg_dir); + + /* Read the first entry to find the number of page table levels. */ + readmem(VADDR, table, &entry, sizeof(entry)); + level = TABLE_LEVEL(entry); + len = TABLE_LENGTH(entry); + + if ((vaddr >> (_SEGMENT_PAGE_SHIFT + (_REGION_INDEX_SHIFT * level)))) { + ERRMSG("Address too big for the number of page table " \ + "levels.\n"); + return NOT_PADDR; + } + + /* + * Walk the region and segment tables. + */ + while (level >= 0) { + entry = _kl_rsg_table_deref_s390x(vaddr, table, len, level); + if (!entry) { + return NOT_PADDR; + } + table = entry & _REGION_ENTRY_ORIGIN; + len = RSG_TABLE_LENGTH(entry); + level--; + } + + /* + * Check if this is a large page. + * if yes, then add the 1MB page offset (PX + BX) and return the value. + * if no, then get the page table entry using PX index. + */ + if (entry & _SEGMENT_ENTRY_LARGE) { + paddr = table + (vaddr & _PAGE_BYTE_INDEX_MASK); + } else { + entry = _kl_pg_table_deref_s390x(vaddr, + entry & _SEGMENT_ENTRY_ORIGIN); + if (!entry) + return NOT_PADDR; + + /* + * Isolate the page origin from the page table entry. + * Add the page offset (BX). + */ + paddr = (entry & _REGION_ENTRY_ORIGIN) + + (vaddr & _BYTE_INDEX_MASK); + } + + return paddr; +} + +unsigned long long +vaddr_to_paddr_s390x(unsigned long vaddr) +{ + unsigned long long paddr; + + paddr = vaddr_to_paddr_general(vaddr); + if (paddr != NOT_PADDR) + return paddr; + + if ((SYMBOL(vmlist) == NOT_FOUND_SYMBOL) + || (OFFSET(vm_struct.addr) == NOT_FOUND_STRUCTURE)) { + ERRMSG("Can't get necessary information for vmalloc " + "translation.\n"); + return NOT_PADDR; + } + + if (is_vmalloc_addr_s390x(vaddr)) { + paddr = vtop_s390x(vaddr); + } + else { + ERRMSG("Can't convert a virtual address(%lx) to " \ + "physical address.\n", vaddr); + return NOT_PADDR; + } + + return paddr; +} + +#endif /* __s390x__ */