diff options
Diffstat (limited to 'sadump_info.c')
-rw-r--r-- | sadump_info.c | 2498 |
1 files changed, 2498 insertions, 0 deletions
diff --git a/sadump_info.c b/sadump_info.c new file mode 100644 index 0000000..dd50d48 --- /dev/null +++ b/sadump_info.c @@ -0,0 +1,2498 @@ +/* + * sadump_info.c + * + * Created by: HATAYAMA, Daisuke <d.hatayama@jp.fujitsu.com> + * + * Copyright (C) 2011 FUJITSU LIMITED + * Copyright (C) 2011 NEC Corporation + * + * 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. + */ +#if defined(__x86__) || defined(__x86_64__) + +#include "makedumpfile.h" +#include "elf_info.h" +#include "print_info.h" +#include "sadump_mod.h" + +#include <arpa/inet.h> /* htonl, htons */ + +#define SADUMP_EFI_GUID_TEXT_REPR_LEN 36 + +#ifdef __x86__ + +#define KEXEC_NOTE_HEAD_BYTES roundup(sizeof(Elf32_Nhdr), 4) + +#endif + +#ifdef __x86_64__ + +#define MEGABYTES(x) ((x) * (1048576)) + +#define KEXEC_NOTE_HEAD_BYTES roundup(sizeof(Elf64_Nhdr), 4) + +#endif + +#define KEXEC_CORE_NOTE_DESC_BYTES roundup(sizeof(struct elf_prstatus), 4) + +#define KEXEC_NOTE_BYTES ((KEXEC_NOTE_HEAD_BYTES * 2) + \ + roundup(KEXEC_CORE_NOTE_NAME_BYTES, 4) + \ + KEXEC_CORE_NOTE_DESC_BYTES ) + +#define for_each_online_cpu(cpu) \ + for (cpu = 0; cpu < max_mask_cpu(); ++cpu) \ + if (is_online_cpu(cpu)) + +enum { + BITPERWORD = BITPERBYTE * sizeof(unsigned long) +}; + +struct sadump_diskset_info { + char *name_memory; + int fd_memory; + struct sadump_part_header *sph_memory; + unsigned long data_offset; +}; + +struct sadump_info { + struct sadump_part_header *sph_memory; + struct sadump_header *sh_memory; + struct sadump_disk_set_header *sdh_memory; + struct sadump_media_header *smh_memory; + struct sadump_diskset_info *diskset_info; + int num_disks; + unsigned long sub_hdr_offset; + uint32_t smram_cpu_state_size; + unsigned long data_offset; + unsigned long long *block_table; + unsigned long *__per_cpu_offset; + unsigned long __per_cpu_load; + FILE *file_elf_note; + char *cpu_online_mask_buf; + size_t cpumask_size; +/* Backup Region, First 640K of System RAM. */ +#define KEXEC_BACKUP_SRC_END 0x0009ffff + unsigned long long backup_src_start; + unsigned long backup_src_size; + unsigned long long backup_offset; + int kdump_backed_up; + mdf_pfn_t max_mapnr; + struct dump_bitmap *ram_bitmap; +}; + +static char *guid_to_str(efi_guid_t *guid, char *buf, size_t buflen); +static struct tm *efi_time_t_to_tm(const efi_time_t *e); +static int verify_magic_number(uint32_t magicnum[DUMP_PART_HEADER_MAGICNUM_SIZE]); +static int read_device(void *buf, size_t bytes, ulong *offset); +static int read_device_diskset(struct sadump_diskset_info *sdi, void *buf, + size_t bytes, ulong *offset); +static int read_sadump_header(char *filename); +static int read_sadump_header_diskset(int diskid, struct sadump_diskset_info *sdi); +static unsigned long long pfn_to_block(mdf_pfn_t pfn); +static int lookup_diskset(unsigned long long whole_offset, int *diskid, + unsigned long long *disk_offset); +static int max_mask_cpu(void); +static int cpu_online_mask_init(void); +static int per_cpu_init(void); +static int get_data_from_elf_note_desc(const char *note_buf, uint32_t n_descsz, + char *name, uint32_t n_type, char **data); +static int alignfile(unsigned long *offset); +static int +write_elf_note_header(char *name, void *data, size_t descsz, uint32_t type, + unsigned long *offset, unsigned long *desc_offset); +static int is_online_cpu(int cpu); +static unsigned long legacy_per_cpu_ptr(unsigned long ptr, int cpu); +static unsigned long per_cpu_ptr(unsigned long ptr, int cpu); +static int get_prstatus_from_crash_notes(int cpu, char *prstatus_buf); +static int cpu_to_apicid(int cpu, int *apicid); +static int get_smram_cpu_state(int apicid, struct sadump_smram_cpu_state *smram); +static int copy_regs_from_prstatus(struct elf_prstatus *prstatus, + const char *prstatus_buf); +static int +copy_regs_from_smram_cpu_state(struct elf_prstatus *prstatus, + const struct sadump_smram_cpu_state *smram); +static void +debug_message_smram_cpu_state(int apicid, struct sadump_smram_cpu_state *s); +static void +debug_message_user_regs_struct(int cpu, struct elf_prstatus *prstatus); +static int get_registers(int cpu, struct elf_prstatus *prstatus); + +static struct sadump_info sadump_info = {}; +static struct sadump_info *si = &sadump_info; + +static inline int +sadump_is_on(char *bitmap, mdf_pfn_t i) +{ + return bitmap[i >> 3] & (1 << (7 - (i & 7))); +} + +static inline int +sadump_is_dumpable(struct dump_bitmap *bitmap, mdf_pfn_t pfn) +{ + off_t offset; + ssize_t rcode; + + if (pfn == 0 || bitmap->no_block != pfn/PFN_BUFBITMAP) { + offset = bitmap->offset + BUFSIZE_BITMAP*(pfn/PFN_BUFBITMAP); + lseek(bitmap->fd, offset, SEEK_SET); + rcode = read(bitmap->fd, bitmap->buf, BUFSIZE_BITMAP); + if (rcode != BUFSIZE_BITMAP) + ERRMSG("Can't read the bitmap(%s). %s\n", + bitmap->file_name, strerror(errno)); + if (pfn == 0) + bitmap->no_block = 0; + else + bitmap->no_block = pfn / PFN_BUFBITMAP; + } + return sadump_is_on(bitmap->buf, pfn % PFN_BUFBITMAP); +} + +static inline int +sadump_is_ram(mdf_pfn_t pfn) +{ + return sadump_is_dumpable(si->ram_bitmap, pfn); +} + +int +check_and_get_sadump_header_info(char *filename) +{ + int i; + + if (!read_sadump_header(filename)) + return FALSE; + + if (info->flag_sadump_diskset && info->flag_sadump == SADUMP_DISKSET) { + + si->diskset_info[0].fd_memory = info->fd_memory; + si->diskset_info[0].sph_memory = si->sph_memory; + si->diskset_info[0].data_offset = si->data_offset; + + for (i = 1; i < si->num_disks; ++i) { + struct sadump_diskset_info *sdi = + &si->diskset_info[i]; + + if ((sdi->fd_memory = + open(sdi->name_memory, O_RDONLY)) < 0) { + ERRMSG("Can't open the dump diskset " + "memory(%s). %s\n", sdi->name_memory, + strerror(errno)); + return FALSE; + } + + if (!read_sadump_header_diskset(i, sdi)) + return FALSE; + } + } + + return TRUE; +} + +static void +reverse_bit(char *buf, int len) +{ + int i; + unsigned char c; + + for (i = 0; i < len; i++) { + c = buf[i]; + c = ((c & 0x55) << 1) | ((c & 0xaa) >> 1); /* Swap 1bit */ + c = ((c & 0x33) << 2) | ((c & 0xcc) >> 2); /* Swap 2bit */ + c = (c << 4) | (c >> 4); /* Swap 4bit */ + buf[i] = c; + } +} + +int +sadump_copy_1st_bitmap_from_memory(void) +{ + struct sadump_header *sh = si->sh_memory; + char buf[si->sh_memory->block_size]; + off_t offset_page; + unsigned long bitmap_offset, bitmap_len; + mdf_pfn_t pfn, pfn_bitmap1; + extern mdf_pfn_t pfn_memhole; + + bitmap_offset = si->sub_hdr_offset + sh->block_size*sh->sub_hdr_size; + bitmap_len = sh->block_size * sh->bitmap_blocks; + + if (lseek(info->fd_memory, bitmap_offset, SEEK_SET) < 0) { + ERRMSG("Can't seek %s. %s\n", + info->name_memory, strerror(errno)); + return FALSE; + } + if (lseek(info->bitmap1->fd, info->bitmap1->offset, SEEK_SET) < 0) { + ERRMSG("Can't seek the bitmap(%s). %s\n", + info->bitmap1->file_name, strerror(errno)); + return FALSE; + } + offset_page = 0; + while (offset_page < bitmap_len) { + if (read(info->fd_memory, buf, sizeof(buf)) != sizeof(buf)) { + ERRMSG("Can't read %s. %s\n", + info->name_memory, strerror(errno)); + return FALSE; + } + /* + * sadump formats associate each bit in a bitmap with + * a physical page in reverse order with the + * kdump-compressed format. We need to change bit + * order to reuse bitmaps in sadump formats in the + * kdump-compressed format. + */ + reverse_bit(buf, sizeof(buf)); + if (write(info->bitmap1->fd, buf, sizeof(buf)) != sizeof(buf)) { + ERRMSG("Can't write the bitmap(%s). %s\n", + info->bitmap1->file_name, strerror(errno)); + return FALSE; + } + offset_page += sizeof(buf); + } + + pfn_bitmap1 = 0; + for (pfn = 0; pfn < info->max_mapnr; ++pfn) { + if (sadump_is_ram(pfn)) + pfn_bitmap1++; + } + pfn_memhole = info->max_mapnr - pfn_bitmap1; + + /* + * kdump uses the first 640kB on the 2nd kernel. But both + * bitmaps should reflect the 1st kernel memory situation. We + * modify bitmap accordingly. + */ + if (si->kdump_backed_up) { + unsigned long long paddr; + mdf_pfn_t pfn, backup_src_pfn; + + for (paddr = si->backup_src_start; + paddr < si->backup_src_start + si->backup_src_size; + paddr += info->page_size) { + + pfn = paddr_to_pfn(paddr); + backup_src_pfn = paddr_to_pfn(paddr + + si->backup_offset - + si->backup_src_start); + + if (is_dumpable(info->bitmap_memory, backup_src_pfn, NULL)) + set_bit_on_1st_bitmap(pfn, NULL); + else + clear_bit_on_1st_bitmap(pfn, NULL); + } + } + + return TRUE; +} + +int +sadump_generate_vmcoreinfo_from_vmlinux(size_t *vmcoreinfo_size) +{ + size_t size; + + if (!info->file_vmcoreinfo) + return FALSE; + + if ((SYMBOL(system_utsname) == NOT_FOUND_SYMBOL) && + (SYMBOL(init_uts_ns) == NOT_FOUND_SYMBOL)) { + ERRMSG("Can't get the symbol of system_utsname.\n"); + return FALSE; + } + + if (get_mem_type() == NOT_FOUND_MEMTYPE) { + ERRMSG("Can't find the memory type.\n"); + return FALSE; + } + + strncpy(info->release, info->system_utsname.release, + strlen(info->system_utsname.release) + 1); + + write_vmcoreinfo_data(); + + size = ftell(info->file_vmcoreinfo); + + *vmcoreinfo_size = size; + + return TRUE; +} + +int +sadump_generate_elf_note_from_dumpfile(void) +{ + size_t size_vmcoreinfo, size_pt_note; + int x_cpu; + unsigned long offset, offset_vmcoreinfo; + char *vmcoreinfo_buf = NULL; + int retval = FALSE; + + if (!per_cpu_init()) + return FALSE; + + if (!(info->file_vmcoreinfo = tmpfile())) { + ERRMSG("Can't create a temporary strings(%s).\n", + FILENAME_VMCOREINFO); + return FALSE; + } + if (!sadump_generate_vmcoreinfo_from_vmlinux(&size_vmcoreinfo)) { + ERRMSG("Can't generate vmcoreinfo data.\n"); + goto error; + } + if ((vmcoreinfo_buf = malloc(size_vmcoreinfo)) == NULL) { + ERRMSG("Can't allocate vmcoreinfo buffer. %s\n", + strerror(errno)); + goto cleanup; + } + rewind(info->file_vmcoreinfo); + if (fread(vmcoreinfo_buf, size_vmcoreinfo, 1, + info->file_vmcoreinfo) != 1) { + ERRMSG("Can't read vmcoreinfo temporary file. %s\n", + strerror(errno)); + goto cleanup; + } + + if (!(si->file_elf_note = tmpfile())) { + ERRMSG("Can't create a temporary elf_note file. %s\n", + strerror(errno)); + goto cleanup; + } + if (!cpu_online_mask_init()) + goto cleanup; + offset = 0; + for_each_online_cpu(x_cpu) { + struct elf_prstatus prstatus; + + memset(&prstatus, 0, sizeof(prstatus)); + + if (!get_registers(x_cpu, &prstatus)) + goto cleanup; + + if (!write_elf_note_header("CORE", &prstatus, sizeof(prstatus), + NT_PRSTATUS, &offset, NULL)) + goto cleanup; + + } + + if (!write_elf_note_header("VMCOREINFO", vmcoreinfo_buf, + size_vmcoreinfo, 0, &offset, + &offset_vmcoreinfo)) + goto cleanup; + + size_pt_note = ftell(si->file_elf_note); + set_pt_note(0, size_pt_note); + set_vmcoreinfo(offset_vmcoreinfo, size_vmcoreinfo); + + retval = TRUE; + +cleanup: + free(vmcoreinfo_buf); + if (info->file_vmcoreinfo) { + fclose(info->file_vmcoreinfo); + info->file_vmcoreinfo = NULL; + } +error: + return retval; +} + +static char * +guid_to_str(efi_guid_t *guid, char *buf, size_t buflen) +{ + snprintf(buf, buflen, + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + htonl(guid->data1), htons(guid->data2), htons(guid->data3), + guid->data4[0], guid->data4[1], guid->data4[2], + guid->data4[3], guid->data4[4], guid->data4[5], + guid->data4[6], guid->data4[7]); + + return buf; +} + +static struct tm * +efi_time_t_to_tm(const efi_time_t *e) +{ + static struct tm t; + time_t ti; + + memset(&t, 0, sizeof(t)); + + t.tm_sec = e->second; + t.tm_min = e->minute; + t.tm_hour = e->hour; + t.tm_mday = e->day; + t.tm_mon = e->month - 1; + t.tm_year = e->year - 1900; + + if (e->timezone != EFI_UNSPECIFIED_TIMEZONE) + t.tm_hour += e->timezone; + + else + DEBUG_MSG("sadump: timezone information is missing\n"); + + ti = mktime(&t); + if (ti == (time_t)-1) + return &t; + + return localtime_r(&ti, &t); +} + +static int +verify_magic_number(uint32_t magicnum[DUMP_PART_HEADER_MAGICNUM_SIZE]) +{ + int i; + + for (i = 1; i < DUMP_PART_HEADER_MAGICNUM_SIZE; ++i) + if (magicnum[i] != (magicnum[i - 1] + 7) * 11) + return FALSE; + + return TRUE; +} + +static int +read_device(void *buf, size_t bytes, ulong *offset) +{ + if (lseek(info->fd_memory, *offset, SEEK_SET) < 0) { + ERRMSG("Can't seek a file(%s). %s\n", + info->name_memory, strerror(errno)); + return FALSE; + } + if (read(info->fd_memory, buf, bytes) != bytes) { + ERRMSG("Can't read a file(%s). %s\n", + info->name_memory, strerror(errno)); + return FALSE; + } + *offset += bytes; + return TRUE; +} + +static int +read_device_diskset(struct sadump_diskset_info *sdi, void *buf, + size_t bytes, unsigned long *offset) +{ + if (lseek(sdi->fd_memory, *offset, SEEK_SET) < 0) { + ERRMSG("Can't seek a file(%s). %s\n", + sdi->name_memory, strerror(errno)); + return FALSE; + } + if (read(sdi->fd_memory, buf, bytes) != bytes) { + ERRMSG("Can't read a file(%s). %s\n", + sdi->name_memory, strerror(errno)); + return FALSE; + } + *offset += bytes; + return TRUE; +} + +static int +read_sadump_header(char *filename) +{ + struct sadump_part_header *sph = NULL; + struct sadump_header *sh = NULL; + struct sadump_disk_set_header *sdh = NULL; + struct sadump_media_header *smh = NULL; + unsigned long offset = 0, sub_hdr_offset; + unsigned long block_size = SADUMP_DEFAULT_BLOCK_SIZE; + unsigned long bitmap_len, dumpable_bitmap_len; + enum sadump_format_type flag_sadump; + uint32_t smram_cpu_state_size = 0; + char guid[SADUMP_EFI_GUID_TEXT_REPR_LEN+1]; + + if ((si->sph_memory = malloc(SADUMP_DEFAULT_BLOCK_SIZE)) == NULL) { + ERRMSG("Can't allocate memory for partition header buffer: " + "%s\n", strerror(errno)); + return FALSE; + } + + if ((si->sh_memory = malloc(SADUMP_DEFAULT_BLOCK_SIZE)) == NULL) { + ERRMSG("Can't allocate memory for dump header buffer: " + "%s\n", strerror(errno)); + return FALSE; + } + + if ((si->sdh_memory = malloc(SADUMP_DEFAULT_BLOCK_SIZE)) == NULL) { + ERRMSG("Can't allocate memory for disk set header buffer: " + "%s\n", strerror(errno)); + return FALSE; + } + + if ((si->smh_memory = malloc(SADUMP_DEFAULT_BLOCK_SIZE)) == NULL) { + ERRMSG("Can't allocate memory for media header buffer: " + "%s\n", strerror(errno)); + return FALSE; + } + + sph = si->sph_memory; + sh = si->sh_memory; + sdh = si->sdh_memory; + smh = si->smh_memory; + +restart: + if (!read_device(sph, block_size, &offset)) + return ERROR; + + if (sph->signature1 == SADUMP_SIGNATURE1 && + sph->signature2 == SADUMP_SIGNATURE2) { + + if (sph->set_disk_set == 0) { + + flag_sadump = SADUMP_SINGLE_PARTITION; + + DEBUG_MSG("sadump: read dump device as single partition\n"); + + } else { + + flag_sadump = SADUMP_DISKSET; + + DEBUG_MSG("sadump: read dump device as diskset\n"); + + } + + } else { + + offset = 0; + + if (!read_device(smh, block_size, &offset)) + return ERROR; + + if (!read_device(sph, block_size, &offset)) + return ERROR; + + if (sph->signature1 != SADUMP_SIGNATURE1 || + sph->signature2 != SADUMP_SIGNATURE2) { + + DEBUG_MSG("sadump: does not have partition header\n"); + + flag_sadump = SADUMP_UNKNOWN; + + DEBUG_MSG("sadump: read dump device as unknown format\n"); + + goto out; + } + + flag_sadump = SADUMP_MEDIA_BACKUP; + + DEBUG_MSG("sadump: read dump device as media backup format\n"); + + } + + if (!verify_magic_number(sph->magicnum)) { + DEBUG_MSG("sadump: invalid magic number\n"); + return FALSE; + } + + if (flag_sadump == SADUMP_DISKSET) { + uint32_t header_blocks; + size_t header_size; + + if (sph->set_disk_set != 1) { + DEBUG_MSG("sadump: id of this disk is %d\n", + sph->set_disk_set); + return FALSE; + } + + if (!read_device(&header_blocks, sizeof(uint32_t), + &offset)) + return FALSE; + + offset -= sizeof(uint32_t); + header_size = header_blocks * block_size; + + if (header_size > block_size) { + sdh = realloc(sdh, header_size); + if (!sdh) { + ERRMSG("Can't allocate memory for disk " + "set memory\n"); + return FALSE; + } + } + + if (!read_device(sdh, header_size, &offset)) + return ERROR; + + DEBUG_MSG("sadump: the diskset consists of %u disks\n", + sdh->disk_num); + + } + + if (!read_device(sh, block_size, &offset)) + return FALSE; + + sub_hdr_offset = offset; + + if (strncmp(sh->signature, SADUMP_SIGNATURE, 8) != 0) { + DEBUG_MSG("sadump: does not have dump header\n"); + return FALSE; + } + + if (flag_sadump == SADUMP_MEDIA_BACKUP) { + + if (memcmp(&sph->sadump_id, &smh->sadump_id, + sizeof(efi_guid_t)) != 0) { + DEBUG_MSG("sadump: system ID mismatch\n"); + DEBUG_MSG(" partition header: %s\n", + guid_to_str(&sph->sadump_id, guid, + sizeof(guid))); + DEBUG_MSG(" media header: %s\n", + guid_to_str(&smh->sadump_id, guid, + sizeof(guid))); + return FALSE; + } + + if (memcmp(&sph->disk_set_id, &smh->disk_set_id, + sizeof(efi_guid_t)) != 0) { + DEBUG_MSG("sadump: disk set ID mismatch\n"); + DEBUG_MSG(" partition header: %s\n", + guid_to_str(&sph->disk_set_id, guid, + sizeof(guid))); + DEBUG_MSG(" media header: %s\n", + guid_to_str(&smh->disk_set_id, guid, + sizeof(guid))); + return FALSE; + } + + if (memcmp(&sph->time_stamp, &smh->time_stamp, + sizeof(efi_time_t)) != 0) { + DEBUG_MSG("sadump: time stamp mismatch\n"); + DEBUG_MSG(" partition header: %s", + asctime(efi_time_t_to_tm(&sph->time_stamp))); + DEBUG_MSG(" media header: %s", + asctime(efi_time_t_to_tm(&smh->time_stamp))); + } + + if (smh->sequential_num != 1) { + DEBUG_MSG("sadump: first media file has sequential " + "number %d\n", smh->sequential_num); + return FALSE; + } + + } + + if (sh->block_size != block_size) { + block_size = sh->block_size; + offset = 0; + goto restart; + } + + if (sh->sub_hdr_size > 0) { + if (!read_device(&smram_cpu_state_size, sizeof(uint32_t), + &offset)) { + DEBUG_MSG("sadump: cannot read SMRAM CPU STATE size\n"); + return FALSE; + } + smram_cpu_state_size /= sh->nr_cpus; + + offset -= sizeof(uint32_t); + offset += sh->sub_hdr_size * block_size; + } + + switch (sh->header_version) { + case 0: + si->max_mapnr = (mdf_pfn_t)(uint64_t)sh->max_mapnr; + break; + default: + ERRMSG("sadump: unsupported header version: %u\n" + "sadump: assuming header version: 1\n", + sh->header_version); + case 1: + si->max_mapnr = (mdf_pfn_t)sh->max_mapnr_64; + break; + } + + if (!sh->bitmap_blocks) { + DEBUG_MSG("sadump: bitmap_blocks is zero\n"); + return FALSE; + } + + if (!sh->dumpable_bitmap_blocks) { + DEBUG_MSG("sadump: dumpable_bitmap_blocks is zero\n"); + return FALSE; + } + + bitmap_len = block_size * sh->bitmap_blocks; + dumpable_bitmap_len = block_size * sh->dumpable_bitmap_blocks; + + si->sub_hdr_offset = sub_hdr_offset; + si->smram_cpu_state_size = smram_cpu_state_size; + si->data_offset = offset + bitmap_len + dumpable_bitmap_len; + +out: + switch (flag_sadump) { + case SADUMP_SINGLE_PARTITION: + DEBUG_MSG("sadump: single partition configuration\n"); + break; + case SADUMP_DISKSET: + DEBUG_MSG("sadump: diskset configuration with %d disks\n", + sdh->disk_num); + break; + case SADUMP_MEDIA_BACKUP: + DEBUG_MSG("sadump: media backup file\n"); + break; + case SADUMP_UNKNOWN: + DEBUG_MSG("sadump: unknown format\n"); + break; + } + + info->flag_sadump = flag_sadump; + + return TRUE; +} + +static int +read_sadump_header_diskset(int diskid, struct sadump_diskset_info *sdi) +{ + struct sadump_part_header *sph = NULL; + unsigned long offset = 0; + char guid[SADUMP_EFI_GUID_TEXT_REPR_LEN+1]; + + if ((sph = malloc(si->sh_memory->block_size)) == NULL) { + ERRMSG("Can't allocate memory for partition header buffer. " + "%s\n", strerror(errno)); + goto error; + } + + if (!read_device_diskset(sdi, sph, si->sh_memory->block_size, + &offset)) + goto error; + + if (sph->signature1 != SADUMP_SIGNATURE1 || + sph->signature2 != SADUMP_SIGNATURE2) { + DEBUG_MSG("sadump: does not have partition header\n"); + goto error; + } + + if (memcmp(&si->sph_memory->sadump_id, &sph->sadump_id, + sizeof(efi_guid_t)) != 0) { + DEBUG_MSG("sadump: system ID mismatch\n"); + DEBUG_MSG(" partition header on disk #1: %s\n", + guid_to_str(&si->sph_memory->sadump_id, guid, + sizeof(guid))); + DEBUG_MSG(" partition header on disk #%d: %s\n", diskid, + guid_to_str(&sph->sadump_id, guid, sizeof(guid))); + goto error; + } + + if (memcmp(&si->sph_memory->disk_set_id, &sph->disk_set_id, + sizeof(efi_guid_t)) != 0) { + DEBUG_MSG("sadump: disk set ID mismatch\n"); + DEBUG_MSG(" partition header on disk #1: %s\n", + guid_to_str(&si->sph_memory->disk_set_id, guid, + sizeof(guid))); + DEBUG_MSG(" partition header on disk #%d: %s\n", diskid, + guid_to_str(&sph->disk_set_id, guid, sizeof(guid))); + goto error; + } + + if (memcmp(&si->sdh_memory->vol_info[diskid-1].id, &sph->vol_id, + sizeof(efi_guid_t)) != 0) { + DEBUG_MSG("sadump: volume ID mismatch\n"); + DEBUG_MSG(" disk set header on disk #1: %s\n", + guid_to_str(&si->sdh_memory->vol_info[diskid-1].id, + guid, sizeof(guid))); + DEBUG_MSG(" partition header on disk #%d: %s\n", + diskid+1, + guid_to_str(&sph->vol_id, guid, sizeof(guid))); + goto error; + } + + if (memcmp(&si->sph_memory->time_stamp, &sph->time_stamp, + sizeof(efi_time_t)) != 0) { + DEBUG_MSG("sadump time stamp mismatch\n"); + DEBUG_MSG(" partition header on disk #1: %s\n", + asctime(efi_time_t_to_tm + (&si->sph_memory->time_stamp))); + DEBUG_MSG(" partition header on disk #%d: %s\n", + diskid, asctime(efi_time_t_to_tm(&sph->time_stamp))); + } + + if (diskid+1 != sph->set_disk_set) { + DEBUG_MSG("sadump: wrong disk order; #%d expected but #%d given\n", + diskid+1, sph->set_disk_set); + goto error; + } + + sdi->sph_memory = sph; + sdi->data_offset = si->sh_memory->block_size; + + return TRUE; + +error: + free(sph); + + return FALSE; +} + +int +sadump_initialize_bitmap_memory(void) +{ + struct sadump_header *sh = si->sh_memory; + struct dump_bitmap *bmp; + unsigned long dumpable_bitmap_offset; + unsigned long long section, max_section; + mdf_pfn_t pfn; + unsigned long long *block_table; + + dumpable_bitmap_offset = + si->sub_hdr_offset + + sh->block_size * (sh->sub_hdr_size + sh->bitmap_blocks); + + bmp = malloc(sizeof(struct dump_bitmap)); + if (bmp == NULL) { + ERRMSG("Can't allocate memory for the memory-bitmap. %s\n", + strerror(errno)); + return FALSE; + } + + bmp->fd = info->fd_memory; + bmp->file_name = info->name_memory; + bmp->no_block = -1; + bmp->offset = dumpable_bitmap_offset; + + bmp->buf = malloc(BUFSIZE_BITMAP); + if (!bmp->buf) { + ERRMSG("Can't allocate memory for the memory-bitmap's buffer. %s\n", + strerror(errno)); + free(bmp); + return FALSE; + } + memset(bmp->buf, 0, BUFSIZE_BITMAP); + + max_section = divideup(si->max_mapnr, SADUMP_PF_SECTION_NUM); + + block_table = calloc(sizeof(unsigned long long), max_section); + if (block_table == NULL) { + ERRMSG("Can't allocate memory for the block_table. %s\n", + strerror(errno)); + free(bmp->buf); + free(bmp); + return FALSE; + } + + for (section = 0; section < max_section; ++section) { + if (section > 0) + block_table[section] = block_table[section-1]; + for (pfn = section * SADUMP_PF_SECTION_NUM; + pfn < (section + 1) * SADUMP_PF_SECTION_NUM; + ++pfn) + if (is_dumpable(bmp, pfn, NULL)) + block_table[section]++; + } + + info->bitmap_memory = bmp; + si->block_table = block_table; + + bmp = malloc(sizeof(struct dump_bitmap)); + if (bmp == NULL) { + ERRMSG("Can't allocate memory for the memory-bitmap. %s\n", + strerror(errno)); + return FALSE; + } + bmp->fd = info->fd_memory; + bmp->file_name = info->name_memory; + bmp->no_block = -1; + bmp->offset = si->sub_hdr_offset + sh->block_size * sh->sub_hdr_size; + + bmp->buf = malloc(BUFSIZE_BITMAP); + if (!bmp->buf) { + ERRMSG("Can't allocate memory for the memory-bitmap's buffer. %s\n", + strerror(errno)); + free(bmp); + return FALSE; + } + memset(bmp->buf, 0, BUFSIZE_BITMAP); + + si->ram_bitmap = bmp; + + /* + * Perform explicitly zero filtering. Without this processing + * crash utility faces different behaviors on reading zero + * pages that are filtered out on the kdump-compressed format + * originating from kdump ELF and from sadump formats: the + * former succeeds in reading zero pages but the latter fails. + */ + for (pfn = 0; pfn < si->max_mapnr; pfn++) { + if (sadump_is_ram(pfn) && + !sadump_is_dumpable(info->bitmap_memory, pfn)) { + info->dump_level |= DL_EXCLUDE_ZERO; + break; + } + } + + return TRUE; +} + +static int +max_mask_cpu(void) +{ + return BITPERBYTE * si->cpumask_size; +} + +static int +cpu_online_mask_init(void) +{ + ulong cpu_online_mask_addr; + + if (si->cpu_online_mask_buf && si->cpumask_size) + return TRUE; + + if (SYMBOL(cpu_online_mask) == NOT_FOUND_SYMBOL || + (SIZE(cpumask) == NOT_FOUND_STRUCTURE && + SIZE(cpumask_t) == NOT_FOUND_STRUCTURE)) + return FALSE; + + si->cpumask_size = SIZE(cpumask) == NOT_FOUND_STRUCTURE + ? SIZE(cpumask_t) + : SIZE(cpumask); + + if (!(si->cpu_online_mask_buf = calloc(1, si->cpumask_size))) { + ERRMSG("Can't allocate cpu_online_mask buffer. %s\n", + strerror(errno)); + return FALSE; + } + + if ((SIZE(cpumask) == NOT_FOUND_STRUCTURE) || + (SYMBOL(__cpu_online_mask) != NOT_FOUND_SYMBOL)) + cpu_online_mask_addr = SYMBOL(cpu_online_mask); + + else { + if (!readmem(VADDR, SYMBOL(cpu_online_mask), + &cpu_online_mask_addr, sizeof(unsigned long))) { + ERRMSG("Can't read cpu_online_mask pointer.\n"); + return FALSE; + } + + } + + if (!readmem(VADDR, cpu_online_mask_addr, si->cpu_online_mask_buf, + si->cpumask_size)) { + ERRMSG("Can't read cpu_online_mask memory.\n"); + return FALSE; + } + + return TRUE; +} + +int +sadump_num_online_cpus(void) +{ + int cpu, count = 0; + + if (!cpu_online_mask_init()) + return FALSE; + + DEBUG_MSG("sadump: online cpus:"); + + for_each_online_cpu(cpu) { + count++; + DEBUG_MSG(" %d", cpu); + } + + DEBUG_MSG("\nsadump: nr_cpus: %d\n", count); + + return count; +} + +int +sadump_set_timestamp(struct timeval *ts) +{ + static struct tm t; + efi_time_t *e = &si->sph_memory->time_stamp; + time_t ti; + + memset(&t, 0, sizeof(t)); + + t.tm_sec = e->second; + t.tm_min = e->minute; + t.tm_hour = e->hour; + t.tm_mday = e->day; + t.tm_mon = e->month - 1; + t.tm_year = e->year - 1900; + + if (e->timezone != EFI_UNSPECIFIED_TIMEZONE) + t.tm_hour += e->timezone; + + else + DEBUG_MSG("sadump: timezone information is missing\n"); + + ti = mktime(&t); + if (ti == (time_t)-1) + return FALSE; + + ts->tv_sec = ti; + ts->tv_usec = 0; + + return TRUE; +} + +mdf_pfn_t +sadump_get_max_mapnr(void) +{ + return si->max_mapnr; +} + +#ifdef __x86_64__ + +/* + * Get address of vector0 interrupt handler (Devide Error) form Interrupt + * Descriptor Table. + */ +static unsigned long +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(PADDR, idtr, &gate, sizeof(gate)); + + 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 cr3) +{ + char cmdline[BUFSIZE], *ptr; + ulong cmdline_vaddr; + ulong cmdline_paddr; + ulong buf_vaddr, buf_paddr; + char *end; + ulong elfcorehdr_addr = 0, elfcorehdr_size = 0; + + if (SYMBOL(saved_command_line) == NOT_FOUND_SYMBOL) { + ERRMSG("Can't get the symbol of saved_command_line.\n"); + return 0; + } + cmdline_vaddr = SYMBOL(saved_command_line); + if ((cmdline_paddr = vtop4_x86_64_pagetable(cmdline_vaddr, cr3)) == NOT_PADDR) + return 0; + + DEBUG_MSG("sadump: cmdline vaddr: %lx\n", cmdline_vaddr); + DEBUG_MSG("sadump: cmdline paddr: %lx\n", cmdline_paddr); + + if (!readmem(PADDR, cmdline_paddr, &buf_vaddr, sizeof(ulong))) + return 0; + + if ((buf_paddr = vtop4_x86_64_pagetable(buf_vaddr, cr3)) == NOT_PADDR) + return 0; + + DEBUG_MSG("sadump: cmdline buf vaddr: %lx\n", buf_vaddr); + DEBUG_MSG("sadump: cmdline buf paddr: %lx\n", buf_paddr); + + memset(cmdline, 0, BUFSIZE); + if (!readmem(PADDR, buf_paddr, cmdline, BUFSIZE)) + return 0; + + ptr = strstr(cmdline, "elfcorehdr="); + if (!ptr) + return 0; + + DEBUG_MSG("sadump: 2nd kernel detected.\n"); + + ptr += strlen("elfcorehdr="); + elfcorehdr_addr = memparse(ptr, &end); + if (*end == '@') { + elfcorehdr_size = elfcorehdr_addr; + elfcorehdr_addr = memparse(end + 1, &end); + } + + DEBUG_MSG("sadump: elfcorehdr_addr: %lx\n", elfcorehdr_addr); + DEBUG_MSG("sadump: 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_in_kdump_kernel(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(PADDR, elfcorehdr, e_ident, EI_NIDENT)) + return FALSE; + + if (e_ident[EI_CLASS] != ELFCLASS64) { + ERRMSG("Only ELFCLASS64 is supportd\n"); + return FALSE; + } + + if (!readmem(PADDR, elfcorehdr, &ehdr, sizeof(ehdr))) + 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) { + ERRMSG("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(PADDR, ptr, &phdr, sizeof(phdr))) + return FALSE; + + ptr += sizeof(phdr); + if (phdr.p_type != PT_NOTE) + continue; + + offset = phdr.p_offset; + if (!readmem(PADDR, offset, &nhdr, sizeof(nhdr))) + return FALSE; + + offset += divideup(sizeof(Elf64_Nhdr), sizeof(Elf64_Word))* + sizeof(Elf64_Word); + memset(name, 0, sizeof(name)); + if (!readmem(PADDR, offset, name, sizeof(name))) + return FALSE; + + if(!strcmp(name, "VMCOREINFO")) { + nhdr_offset = offset; + break; + } + } + + if (!nhdr_offset) + return FALSE; + + *addr = nhdr_offset + + divideup(nhdr.n_namesz, sizeof(Elf64_Word))* + sizeof(Elf64_Word); + *len = nhdr.n_descsz; + + DEBUG_MSG("sadump: vmcoreinfo addr: %lx\n", *addr); + DEBUG_MSG("sadump: 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. + */ +int +get_kaslr_offset_from_vmcoreinfo(ulong cr3, ulong *kaslr_offset, + ulong *phys_base) +{ + ulong elfcorehdr_addr = 0; + ulong vmcoreinfo_addr; + int vmcoreinfo_len; + char *buf, *pos; + int ret = FALSE; + + elfcorehdr_addr = get_elfcorehdr(cr3); + if (!elfcorehdr_addr) + return FALSE; + + if (!get_vmcoreinfo_in_kdump_kernel(elfcorehdr_addr, &vmcoreinfo_addr, + &vmcoreinfo_len)) + return FALSE; + + if (!vmcoreinfo_len) + return FALSE; + + DEBUG_MSG("sadump: Find vmcoreinfo in kdump memory\n"); + + if (!(buf = malloc(vmcoreinfo_len))) { + ERRMSG("Can't allocate vmcoreinfo buffer.\n"); + return FALSE; + } + + if (!readmem(PADDR, vmcoreinfo_addr, buf, vmcoreinfo_len)) + goto finish; + + pos = strstr(buf, STR_NUMBER("phys_base")); + if (!pos) + goto finish; + *phys_base = strtoull(pos + strlen(STR_NUMBER("phys_base")), NULL, 0); + + pos = strstr(buf, STR_KERNELOFFSET); + if (!pos) + goto finish; + *kaslr_offset = strtoull(pos + strlen(STR_KERNELOFFSET), NULL, 16); + ret = TRUE; + +finish: + free(buf); + return ret; +} + +/* + * Calculate kaslr_offset and phys_base + * + * kaslr_offset: + * The difference between original address in 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, 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 (info->page_shift) +#define PTI_USER_PGTABLE_MASK (1 << PTI_USER_PGTABLE_BIT) +#define CR3_PCID_MASK 0xFFFull +int +calc_kaslr_offset(void) +{ + struct sadump_header *sh = si->sh_memory; + uint64_t idtr = 0, cr3 = 0, idtr_paddr; + struct sadump_smram_cpu_state smram, zero; + int apicid; + unsigned long divide_error_vmcore, divide_error_vmlinux; + unsigned long kaslr_offset, phys_base; + unsigned long kaslr_offset_kdump, phys_base_kdump; + + memset(&zero, 0, sizeof(zero)); + for (apicid = 0; apicid < sh->nr_cpus; ++apicid) { + if (!get_smram_cpu_state(apicid, &smram)) { + ERRMSG("get_smram_cpu_state error\n"); + return FALSE; + } + + if (memcmp(&smram, &zero, sizeof(smram)) != 0) + break; + } + if (apicid >= sh->nr_cpus) { + ERRMSG("Can't get smram state\n"); + return FALSE; + } + + idtr = ((uint64_t)smram.IdtUpper)<<32 | (uint64_t)smram.IdtLower; + if ((SYMBOL(pti_init) != NOT_FOUND_SYMBOL) || + (SYMBOL(kaiser_init) != NOT_FOUND_SYMBOL)) + cr3 = smram.Cr3 & ~(CR3_PCID_MASK|PTI_USER_PGTABLE_MASK); + else + cr3 = smram.Cr3 & ~CR3_PCID_MASK; + + /* Convert virtual address of IDT table to physical address */ + if ((idtr_paddr = vtop4_x86_64_pagetable(idtr, cr3)) == NOT_PADDR) + return FALSE; + + /* Now we can calculate kaslr_offset and phys_base */ + divide_error_vmlinux = SYMBOL(divide_error); + divide_error_vmcore = get_vec0_addr(idtr_paddr); + kaslr_offset = divide_error_vmcore - divide_error_vmlinux; + phys_base = idtr_paddr - + (SYMBOL(idt_table) + kaslr_offset - __START_KERNEL_map); + + info->kaslr_offset = kaslr_offset; + info->phys_base = phys_base; + + DEBUG_MSG("sadump: idtr=%" PRIx64 "\n", idtr); + DEBUG_MSG("sadump: cr3=%" PRIx64 "\n", cr3); + DEBUG_MSG("sadump: idtr(phys)=%" PRIx64 "\n", idtr_paddr); + DEBUG_MSG("sadump: devide_error(vmlinux)=%lx\n", + divide_error_vmlinux); + DEBUG_MSG("sadump: devide_error(vmcore)=%lx\n", + divide_error_vmcore); + + /* Reload symbol */ + if (!get_symbol_info()) + return FALSE; + + /* + * 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(cr3, &kaslr_offset_kdump, + &phys_base_kdump)) { + info->kaslr_offset = kaslr_offset_kdump; + info->phys_base = phys_base_kdump; + + /* Reload symbol */ + if (!get_symbol_info()) + return FALSE; + } + + DEBUG_MSG("sadump: kaslr_offset=%lx\n", info->kaslr_offset); + DEBUG_MSG("sadump: phys_base=%lx\n", info->phys_base); + + return TRUE; +} + +int +sadump_virt_phys_base(void) +{ + char buf[BUFSIZE]; + unsigned long phys, linux_banner_phys; + + if (SYMBOL(linux_banner) == NOT_FOUND_SYMBOL) { + DEBUG_MSG("sadump: symbol linux_banner is not found\n"); + goto failed; + } + + linux_banner_phys = SYMBOL(linux_banner) - __START_KERNEL_map; + + if (readmem(PADDR, linux_banner_phys + info->phys_base, buf, + strlen("Linux version")) && STRNEQ(buf, "Linux version")) + return TRUE; + + for (phys = (-MEGABYTES(16)); phys != MEGABYTES(16+1); + phys += MEGABYTES(1)) { + if (readmem(PADDR, linux_banner_phys + phys, buf, + strlen("Linux version")) && + STRNEQ(buf, "Linux version")) { + DEBUG_MSG("sadump: phys_base: %lx %s\n", phys, + info->phys_base != phys ? "override" : ""); + info->phys_base = phys; + return TRUE; + } + } + +failed: + if (calc_kaslr_offset()) + return TRUE; + + info->phys_base = 0; + + DEBUG_MSG("sadump: failed to calculate phys_base; default to 0\n"); + + return FALSE; +} + +#endif /* __x86_64__ */ + +int +readpage_sadump(unsigned long long paddr, void *bufptr) +{ + mdf_pfn_t pfn; + unsigned long long block, whole_offset, perdisk_offset; + int fd_memory; + + if (si->kdump_backed_up && + paddr >= si->backup_src_start && + paddr < si->backup_src_start + si->backup_src_size) + paddr += si->backup_offset - si->backup_src_start; + + pfn = paddr_to_pfn(paddr); + + if (pfn >= si->max_mapnr) + return FALSE; + + if (!sadump_is_ram(pfn)) { + ERRMSG("pfn(%llx) is not ram.\n", pfn); + return FALSE; + } + + if (!sadump_is_dumpable(info->bitmap_memory, pfn)) { + memset(bufptr, 0, info->page_size); + return TRUE; + } + + block = pfn_to_block(pfn); + whole_offset = block * si->sh_memory->block_size; + + if (info->flag_sadump == SADUMP_DISKSET) { + int diskid; + + if (!lookup_diskset(whole_offset, &diskid, &perdisk_offset)) + return FALSE; + + fd_memory = si->diskset_info[diskid].fd_memory; + perdisk_offset += si->diskset_info[diskid].data_offset; + + } else { + fd_memory = info->fd_memory; + perdisk_offset = whole_offset + si->data_offset; + + } + + if (lseek(fd_memory, perdisk_offset, SEEK_SET) < 0) + return FALSE; + + if (read(fd_memory, bufptr, info->page_size) != info->page_size) + return FALSE; + + return TRUE; +} + +int +sadump_check_debug_info(void) +{ + if (SYMBOL(linux_banner) == NOT_FOUND_SYMBOL) + return FALSE; + if (SYMBOL(bios_cpu_apicid) == NOT_FOUND_SYMBOL && + SYMBOL(x86_bios_cpu_apicid) == NOT_FOUND_SYMBOL) + return FALSE; + if (SYMBOL(x86_bios_cpu_apicid) != NOT_FOUND_SYMBOL && + (SYMBOL(x86_bios_cpu_apicid_early_ptr) == NOT_FOUND_SYMBOL || + SYMBOL(x86_bios_cpu_apicid_early_map) == NOT_FOUND_SYMBOL)) + return FALSE; + if (SYMBOL(crash_notes) == NOT_FOUND_SYMBOL) + return FALSE; + if (SIZE(percpu_data) == NOT_FOUND_STRUCTURE && + SYMBOL(__per_cpu_load) == NOT_FOUND_SYMBOL) + return FALSE; + if (SYMBOL(__per_cpu_load) != NOT_FOUND_SYMBOL && + (SYMBOL(__per_cpu_offset) == NOT_FOUND_SYMBOL && + ARRAY_LENGTH(__per_cpu_offset) == NOT_FOUND_STRUCTURE)) + return FALSE; + if (SIZE(elf_prstatus) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(elf_prstatus.pr_reg) == NOT_FOUND_STRUCTURE) + return FALSE; +#ifdef __x86__ + if (OFFSET(user_regs_struct.bx) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.cx) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.dx) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.si) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.di) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.bp) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.ax) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.ds) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.es) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.fs) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.gs) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.orig_ax) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.ip) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.cs) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.flags) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.sp) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.ss) == NOT_FOUND_STRUCTURE) + return FALSE; +#elif defined(__x86_64__) + if (OFFSET(user_regs_struct.r15) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.r14) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.r13) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.r12) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.bp) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.bx) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.r11) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.r10) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.r9) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.r8) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.ax) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.cx) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.dx) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.si) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.di) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.orig_ax) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.ip) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.cs) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.flags) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.sp) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.ss) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.fs_base) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.gs_base) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.ds) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.es) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.fs) == NOT_FOUND_STRUCTURE) + return FALSE; + if (OFFSET(user_regs_struct.gs) == NOT_FOUND_STRUCTURE) + return FALSE; +#endif /* __x86_64__ */ + return TRUE; +} + +static unsigned long long +pfn_to_block(mdf_pfn_t pfn) +{ + unsigned long long block, section, p; + + section = pfn / SADUMP_PF_SECTION_NUM; + + if (section) + block = si->block_table[section - 1]; + else + block = 0; + + for (p = section * SADUMP_PF_SECTION_NUM; p < pfn; ++p) + if (sadump_is_dumpable(info->bitmap_memory, p)) + block++; + + return block; +} + +static int +lookup_diskset(unsigned long long whole_offset, int *diskid, + unsigned long long *disk_offset) +{ + unsigned long long offset = whole_offset; + int i; + + for (i = 0; i < si->num_disks; ++i) { + struct sadump_diskset_info *sdi = &si->diskset_info[i]; + unsigned long long used_device_i, data_offset_i, ram_size; + + used_device_i = sdi->sph_memory->used_device; + data_offset_i = sdi->data_offset; + + ram_size = used_device_i - data_offset_i; + + if (offset < ram_size) + break; + offset -= ram_size; + } + + if (i == si->num_disks) + return FALSE; + + *diskid = i; + *disk_offset = offset; + + return TRUE; +} + +static int +per_cpu_init(void) +{ + size_t __per_cpu_offset_size; + int i; + + if (SIZE(percpu_data) != NOT_FOUND_STRUCTURE) + return TRUE; + + __per_cpu_offset_size = + ARRAY_LENGTH(__per_cpu_offset) * sizeof(unsigned long); + + if (!(si->__per_cpu_offset = malloc(__per_cpu_offset_size))) { + ERRMSG("Can't allocate __per_cpu_offset buffer.\n"); + return FALSE; + } + + if (!readmem(VADDR, SYMBOL(__per_cpu_offset), si->__per_cpu_offset, + __per_cpu_offset_size)) { + ERRMSG("Can't read __per_cpu_offset memory.\n"); + return FALSE; + } + + if (!readmem(VADDR, SYMBOL(__per_cpu_load), &si->__per_cpu_load, + sizeof(unsigned long))) { + ERRMSG("Can't read __per_cpu_load memory.\n"); + return FALSE; + } + + DEBUG_MSG("sadump: __per_cpu_load: %#lx\n", si->__per_cpu_load); + DEBUG_MSG("sadump: __per_cpu_offset: LENGTH: %ld\n", + ARRAY_LENGTH(__per_cpu_offset)); + + for (i = 0; i < ARRAY_LENGTH(__per_cpu_offset); ++i) { + DEBUG_MSG("sadump: __per_cpu_offset[%d]: %#lx\n", i, + si->__per_cpu_offset[i]); + } + + return TRUE; +} + +static int +get_data_from_elf_note_desc(const char *note_buf, uint32_t n_descsz, + char *name, uint32_t n_type, char **data) +{ + Elf32_Nhdr *note32; + char *note_name; + + note32 = (Elf32_Nhdr *)note_buf; + note_name = (char *)(note32 + 1); + + if (note32->n_type != n_type || + note32->n_namesz != strlen(name) + 1 || + note32->n_descsz != n_descsz || + strncmp(note_name, name, note32->n_namesz)) + return FALSE; + + *data = (char *)note_buf + + roundup(sizeof(Elf32_Nhdr) + note32->n_namesz, 4); + + return TRUE; +} + +static int +alignfile(unsigned long *offset) +{ + char nullbyte = '\0'; + unsigned int len; + + len = roundup(*offset, 4) - *offset; + if (fwrite(&nullbyte, 1, len, si->file_elf_note) != len) { + ERRMSG("Can't write elf_note file. %s\n", strerror(errno)); + return FALSE; + } + *offset += len; + return TRUE; +} + +static int +write_elf_note_header(char *name, void *data, size_t descsz, uint32_t type, + unsigned long *offset, unsigned long *desc_offset) +{ + Elf32_Nhdr nhdr; + + nhdr.n_namesz = strlen(name) + 1; + nhdr.n_descsz = descsz; + nhdr.n_type = type; + + if (fwrite(&nhdr, sizeof(nhdr), 1, si->file_elf_note) != 1) { + ERRMSG("Can't write elf_note file. %s\n", strerror(errno)); + return FALSE; + } + *offset += sizeof(nhdr); + + if (fwrite(name, nhdr.n_namesz, 1, si->file_elf_note) != 1) { + ERRMSG("Can't write elf_note file. %s\n", strerror(errno)); + return FALSE; + } + *offset += nhdr.n_namesz; + if (!alignfile(offset)) + return FALSE; + + if (desc_offset) + *desc_offset = *offset; + + if (fwrite(data, nhdr.n_descsz, 1, si->file_elf_note) != 1) { + ERRMSG("Can't write elf_note file. %s\n", strerror(errno)); + return FALSE; + } + *offset += nhdr.n_descsz; + if (!alignfile(offset)) + return FALSE; + + return TRUE; +} + +static int +is_online_cpu(int cpu) +{ + unsigned long mask; + + if (cpu < 0 || cpu >= max_mask_cpu()) + return FALSE; + + mask = ULONG(si->cpu_online_mask_buf + + (cpu / BITPERWORD) * sizeof(unsigned long)); + + return (mask & (1UL << (cpu % BITPERWORD))) ? TRUE : FALSE; +} + +static unsigned long +legacy_per_cpu_ptr(unsigned long ptr, int cpu) +{ + unsigned long addr; + + if (!is_online_cpu(cpu)) + return 0UL; + + if (!readmem(VADDR, ~ptr + cpu*sizeof(unsigned long), &addr, + sizeof(addr))) + return 0UL; + + return addr; +} + +static unsigned long +per_cpu_ptr(unsigned long ptr, int cpu) +{ + if (!is_online_cpu(cpu)) + return 0UL; + + if (si->__per_cpu_offset[cpu] == si->__per_cpu_load) + return 0UL; + + return ptr + si->__per_cpu_offset[cpu]; +} + +static int +get_prstatus_from_crash_notes(int cpu, char *prstatus_buf) +{ + unsigned long crash_notes_vaddr, percpu_addr; + char note_buf[KEXEC_NOTE_BYTES], zero_buf[KEXEC_NOTE_BYTES]; + char *prstatus_ptr; + + if (!is_online_cpu(cpu)) + return FALSE; + + if (SYMBOL(crash_notes) == NOT_FOUND_SYMBOL) + return FALSE; + + if (!readmem(VADDR, SYMBOL(crash_notes), &crash_notes_vaddr, + sizeof(crash_notes_vaddr))) + return FALSE; + + if (!crash_notes_vaddr) { + DEBUG_MSG("sadump: crash_notes %d is NULL\n", cpu); + return FALSE; + } + + memset(zero_buf, 0, KEXEC_NOTE_BYTES); + + percpu_addr = SIZE(percpu_data) != NOT_FOUND_STRUCTURE + ? legacy_per_cpu_ptr(crash_notes_vaddr, cpu) + : per_cpu_ptr(crash_notes_vaddr, cpu); + + if (!readmem(VADDR, percpu_addr, note_buf, KEXEC_NOTE_BYTES)) + return FALSE; + + if (memcmp(note_buf, zero_buf, KEXEC_NOTE_BYTES) == 0) + return FALSE; + + if (!get_data_from_elf_note_desc(note_buf, SIZE(elf_prstatus), "CORE", + NT_PRSTATUS, (void *)&prstatus_ptr)) + return FALSE; + + memcpy(prstatus_buf, prstatus_ptr, SIZE(elf_prstatus)); + + return TRUE; +} + +static int +cpu_to_apicid(int cpu, int *apicid) +{ + if (SYMBOL(bios_cpu_apicid) != NOT_FOUND_SYMBOL) { + uint8_t apicid_u8; + + if (!readmem(VADDR, SYMBOL(bios_cpu_apicid)+cpu*sizeof(uint8_t), + &apicid_u8, sizeof(uint8_t))) + return FALSE; + + *apicid = (int)apicid_u8; + + DEBUG_MSG("sadump: apicid %u for cpu %d from " + "bios_cpu_apicid\n", apicid_u8, cpu); + + } else if (SYMBOL(x86_bios_cpu_apicid) != NOT_FOUND_SYMBOL) { + uint16_t apicid_u16; + unsigned long early_ptr, apicid_addr; + + if (!readmem(VADDR, SYMBOL(x86_bios_cpu_apicid_early_ptr), + &early_ptr, sizeof(early_ptr))) + return FALSE; + /* + * Note: SYMBOL(name) value is adjusted by info->kaslr_offset, + * but per_cpu symbol does not need to be adjusted becasue it + * is not affected by kaslr. + */ + apicid_addr = early_ptr + ? SYMBOL(x86_bios_cpu_apicid_early_map)+cpu*sizeof(uint16_t) + : per_cpu_ptr(SYMBOL(x86_bios_cpu_apicid) - info->kaslr_offset, cpu); + + if (!readmem(VADDR, apicid_addr, &apicid_u16, sizeof(uint16_t))) + return FALSE; + + *apicid = (int)apicid_u16; + + DEBUG_MSG("sadump: apicid %u for cpu %d from " + "x86_bios_cpu_apicid\n", apicid_u16, cpu); + + } else { + + ERRMSG("sadump: no symbols for access to acpidid\n"); + + return FALSE; + } + + return TRUE; +} + +static int +get_smram_cpu_state(int apicid, struct sadump_smram_cpu_state *smram) +{ + unsigned long offset; + + if (!si->sub_hdr_offset || !si->smram_cpu_state_size || + apicid >= si->sh_memory->nr_cpus) + return FALSE; + + offset = si->sub_hdr_offset + sizeof(uint32_t) + + si->sh_memory->nr_cpus * sizeof(struct sadump_apic_state); + + if (lseek(info->fd_memory, offset+apicid*si->smram_cpu_state_size, + SEEK_SET) < 0) + DEBUG_MSG("sadump: cannot lseek smram cpu state in dump sub " + "header\n"); + + if (read(info->fd_memory, smram, si->smram_cpu_state_size) != + si->smram_cpu_state_size) + DEBUG_MSG("sadump: cannot read smram cpu state in dump sub " + "header\n"); + + return TRUE; +} + +#ifdef __x86__ + +static int +copy_regs_from_prstatus(struct elf_prstatus *prstatus, + const char *prstatus_buf) +{ + struct user_regs_struct *r = &prstatus->pr_reg; + const char *pr_reg_buf = prstatus_buf + OFFSET(elf_prstatus.pr_reg); + + r->bx = ULONG(pr_reg_buf + OFFSET(user_regs_struct.bx)); + r->cx = ULONG(pr_reg_buf + OFFSET(user_regs_struct.cx)); + r->dx = ULONG(pr_reg_buf + OFFSET(user_regs_struct.dx)); + r->si = ULONG(pr_reg_buf + OFFSET(user_regs_struct.si)); + r->di = ULONG(pr_reg_buf + OFFSET(user_regs_struct.di)); + r->bp = ULONG(pr_reg_buf + OFFSET(user_regs_struct.bp)); + r->ax = ULONG(pr_reg_buf + OFFSET(user_regs_struct.ax)); + r->ds = ULONG(pr_reg_buf + OFFSET(user_regs_struct.ds)); + r->es = ULONG(pr_reg_buf + OFFSET(user_regs_struct.es)); + r->fs = ULONG(pr_reg_buf + OFFSET(user_regs_struct.fs)); + r->gs = ULONG(pr_reg_buf + OFFSET(user_regs_struct.gs)); + r->orig_ax = ULONG(pr_reg_buf + OFFSET(user_regs_struct.orig_ax)); + r->ip = ULONG(pr_reg_buf + OFFSET(user_regs_struct.ip)); + r->cs = ULONG(pr_reg_buf + OFFSET(user_regs_struct.cs)); + r->flags = ULONG(pr_reg_buf + OFFSET(user_regs_struct.flags)); + r->sp = ULONG(pr_reg_buf + OFFSET(user_regs_struct.sp)); + r->ss = ULONG(pr_reg_buf + OFFSET(user_regs_struct.ss)); + + return TRUE; +} + +static int +copy_regs_from_smram_cpu_state(struct elf_prstatus *prstatus, + const struct sadump_smram_cpu_state *smram) +{ + struct user_regs_struct *regs = &prstatus->pr_reg; + + regs->bx = smram->RbxLower; + regs->cx = smram->RcxLower; + regs->dx = smram->RdxLower; + regs->si = smram->RsiLower; + regs->di = smram->RdiLower; + regs->bp = smram->RbpLower; + regs->ax = smram->RaxLower; + regs->ds = smram->Ds & 0xffff; + regs->es = smram->Es & 0xffff; + regs->fs = smram->Fs & 0xffff; + regs->gs = smram->Gs & 0xffff; + regs->orig_ax = smram->RaxLower; + regs->ip = (uint32_t)smram->Rip; + regs->cs = smram->Cs & 0xffff; + regs->flags = (uint32_t)smram->Rflags; + regs->sp = smram->RspLower; + regs->ss = smram->Ss & 0xffff; + + return TRUE; +} + +static void +debug_message_user_regs_struct(int cpu, struct elf_prstatus *prstatus) +{ + struct user_regs_struct *r = &prstatus->pr_reg; + + DEBUG_MSG( + "sadump: CPU: %d\n" + " BX: %08lx CX: %08lx DX: %08lx SI: %08lx\n" + " DI: %08lx BP: %08lx AX: %08lx ORIG_AX: %08lx\n" + " DS: %04lx ES: %04lx FS: %04lx GS: %04lx CS: %04lx SS: %04lx\n" + " IP: %08lx FLAGS: %04lx SP: %08lx\n", + cpu, + r->bx, r->cx, r->dx, r->si, + r->di, r->bp, r->ax, r->orig_ax, + r->ds, r->es, r->fs, r->gs, r->cs, r->ss, + r->ip, r->flags, r->sp); +} + +#elif defined(__x86_64__) + +static int +copy_regs_from_prstatus(struct elf_prstatus *prstatus, + const char *prstatus_buf) +{ + struct user_regs_struct *r = &prstatus->pr_reg; + const char *pr_reg_buf = prstatus_buf + OFFSET(elf_prstatus.pr_reg); + + r->r15 = ULONG(pr_reg_buf + OFFSET(user_regs_struct.r15)); + r->r14 = ULONG(pr_reg_buf + OFFSET(user_regs_struct.r14)); + r->r13 = ULONG(pr_reg_buf + OFFSET(user_regs_struct.r13)); + r->bp = ULONG(pr_reg_buf + OFFSET(user_regs_struct.bp)); + r->bx = ULONG(pr_reg_buf + OFFSET(user_regs_struct.bx)); + r->r11 = ULONG(pr_reg_buf + OFFSET(user_regs_struct.r11)); + r->r10 = ULONG(pr_reg_buf + OFFSET(user_regs_struct.r10)); + r->r9 = ULONG(pr_reg_buf + OFFSET(user_regs_struct.r9)); + r->r8 = ULONG(pr_reg_buf + OFFSET(user_regs_struct.r8)); + r->ax = ULONG(pr_reg_buf + OFFSET(user_regs_struct.ax)); + r->cx = ULONG(pr_reg_buf + OFFSET(user_regs_struct.cx)); + r->dx = ULONG(pr_reg_buf + OFFSET(user_regs_struct.dx)); + r->si = ULONG(pr_reg_buf + OFFSET(user_regs_struct.si)); + r->di = ULONG(pr_reg_buf + OFFSET(user_regs_struct.di)); + r->orig_ax = ULONG(pr_reg_buf + OFFSET(user_regs_struct.orig_ax)); + r->ip = ULONG(pr_reg_buf + OFFSET(user_regs_struct.ip)); + r->cs = ULONG(pr_reg_buf + OFFSET(user_regs_struct.cs)); + r->flags = ULONG(pr_reg_buf + OFFSET(user_regs_struct.flags)); + r->sp = ULONG(pr_reg_buf + OFFSET(user_regs_struct.sp)); + r->ss = ULONG(pr_reg_buf + OFFSET(user_regs_struct.ss)); + r->fs_base = ULONG(pr_reg_buf + OFFSET(user_regs_struct.fs_base)); + r->gs_base = ULONG(pr_reg_buf + OFFSET(user_regs_struct.gs_base)); + r->ds = ULONG(pr_reg_buf + OFFSET(user_regs_struct.ds)); + r->es = ULONG(pr_reg_buf + OFFSET(user_regs_struct.es)); + r->fs = ULONG(pr_reg_buf + OFFSET(user_regs_struct.fs)); + r->gs = ULONG(pr_reg_buf + OFFSET(user_regs_struct.gs)); + + return TRUE; +} + +static int +copy_regs_from_smram_cpu_state(struct elf_prstatus *prstatus, + const struct sadump_smram_cpu_state *smram) +{ + struct user_regs_struct *regs = &prstatus->pr_reg; + + regs->r15 = ((uint64_t)smram->R15Upper<<32)+smram->R15Lower; + regs->r14 = ((uint64_t)smram->R14Upper<<32)+smram->R14Lower; + regs->r13 = ((uint64_t)smram->R13Upper<<32)+smram->R13Lower; + regs->r12 = ((uint64_t)smram->R12Upper<<32)+smram->R12Lower; + regs->bp = ((uint64_t)smram->RbpUpper<<32)+smram->RbpLower; + regs->bx = ((uint64_t)smram->RbxUpper<<32)+smram->RbxLower; + regs->r11 = ((uint64_t)smram->R11Upper<<32)+smram->R11Lower; + regs->r10 = ((uint64_t)smram->R10Upper<<32)+smram->R10Lower; + regs->r9 = ((uint64_t)smram->R9Upper<<32)+smram->R9Lower; + regs->r8 = ((uint64_t)smram->R8Upper<<32)+smram->R8Lower; + regs->ax = ((uint64_t)smram->RaxUpper<<32)+smram->RaxLower; + regs->cx = ((uint64_t)smram->RcxUpper<<32)+smram->RcxLower; + regs->dx = ((uint64_t)smram->RdxUpper<<32)+smram->RdxLower; + regs->si = ((uint64_t)smram->RsiUpper<<32)+smram->RsiLower; + regs->di = ((uint64_t)smram->RdiUpper<<32)+smram->RdiLower; + regs->orig_ax = ((uint64_t)smram->RaxUpper<<32)+smram->RaxLower; + regs->ip = smram->Rip; + regs->cs = smram->Cs; + regs->flags = smram->Rflags; + regs->sp = ((uint64_t)smram->RspUpper<<32)+smram->RspLower; + regs->ss = smram->Ss; + regs->fs_base = 0; + regs->gs_base = 0; + regs->ds = smram->Ds; + regs->es = smram->Es; + regs->fs = smram->Fs; + regs->gs = smram->Gs; + + return TRUE; +} + +static void +debug_message_user_regs_struct(int cpu, struct elf_prstatus *prstatus) +{ + struct user_regs_struct *r = &prstatus->pr_reg; + + DEBUG_MSG( + "sadump: CPU: %d\n" + " R15: %016llx R14: %016llx R13: %016llx\n" + " R12: %016llx RBP: %016llx RBX: %016llx\n" + " R11: %016llx R10: %016llx R9: %016llx\n" + " R8: %016llx RAX: %016llx RCX: %016llx\n" + " RDX: %016llx RSI: %016llx RDI: %016llx\n" + " ORIG_RAX: %016llx RIP: %016llx\n" + " CS: %04lx FLAGS: %08llx RSP: %016llx\n" + " SS: %04lx FS_BASE: %04lx GS_BASE: %04lx\n" + " DS: %04lx ES: %04lx FS: %04lx GS: %04lx\n", + cpu, + (unsigned long long)r->r15, (unsigned long long)r->r14, + (unsigned long long)r->r13, (unsigned long long)r->r12, + (unsigned long long)r->bp, (unsigned long long)r->bx, + (unsigned long long)r->r11, (unsigned long long)r->r10, + (unsigned long long)r->r9, (unsigned long long)r->r8, + (unsigned long long)r->ax, (unsigned long long)r->cx, + (unsigned long long)r->dx, (unsigned long long)r->si, + (unsigned long long)r->di, + (unsigned long long)r->orig_ax, + (unsigned long long)r->ip, r->cs, + (unsigned long long)r->flags, (unsigned long long)r->sp, + r->ss, r->fs_base, r->gs_base, r->ds, r->es, r->fs, + r->gs); +} + +#endif /* __x86_64__ */ + +static void +debug_message_smram_cpu_state(int apicid, struct sadump_smram_cpu_state *s) +{ + DEBUG_MSG( + "sadump: APIC ID: %d\n" + " RIP: %016llx RSP: %08x%08x RBP: %08x%08x\n" + " RAX: %08x%08x RBX: %08x%08x RCX: %08x%08x\n" + " RDX: %08x%08x RSI: %08x%08x RDI: %08x%08x\n" + " R08: %08x%08x R09: %08x%08x R10: %08x%08x\n" + " R11: %08x%08x R12: %08x%08x R13: %08x%08x\n" + " R14: %08x%08x R15: %08x%08x\n" + " SMM REV: %08x SMM BASE %08x\n" + " CS : %08x DS: %08x SS: %08x ES: %08x FS: %08x\n" + " GS : %08x\n" + " CR0: %016llx CR3: %016llx CR4: %08x\n" + " GDT: %08x%08x LDT: %08x%08x IDT: %08x%08x\n" + " GDTlim: %08x LDTlim: %08x IDTlim: %08x\n" + " LDTR: %08x TR: %08x RFLAGS: %016llx\n" + " EPTP: %016llx EPTP_SETTING: %08x\n" + " DR6: %016llx DR7: %016llx\n" + " Ia32Efer: %016llx\n" + " IoMemAddr: %08x%08x IoEip: %016llx\n" + " IoMisc: %08x LdtInfo: %08x\n" + " IoInstructionRestart: %04x AutoHaltRestart: %04x\n", + apicid, + (unsigned long long)s->Rip, s->RspUpper, s->RspLower, s->RbpUpper, s->RbpLower, + s->RaxUpper, s->RaxLower, s->RbxUpper, s->RbxLower, s->RcxUpper, s->RcxLower, + s->RdxUpper, s->RdxLower, s->RsiUpper, s->RsiLower, s->RdiUpper, s->RdiLower, + s->R8Upper, s->R8Lower, s->R9Upper, s->R9Lower, s->R10Upper, s->R10Lower, + s->R11Upper, s->R11Lower, s->R12Upper, s->R12Lower, s->R13Upper, s->R13Lower, + s->R14Upper, s->R14Lower, s->R15Upper, s->R15Lower, + s->SmmRevisionId, s->Smbase, + s->Cs, s->Ds, s->Ss, s->Es, s->Fs, s->Gs, + (unsigned long long)s->Cr0, (unsigned long long)s->Cr3, s->Cr4, + s->GdtUpper, s->GdtLower, s->LdtUpper, s->LdtLower, s->IdtUpper, s->IdtLower, + s->GdtLimit, s->LdtLimit, s->IdtLimit, + s->Ldtr, s->Tr, (unsigned long long)s->Rflags, + (unsigned long long)s->Eptp, s->EptpSetting, + (unsigned long long)s->Dr6, (unsigned long long)s->Dr7, + (unsigned long long)s->Ia32Efer, + s->IoMemAddrUpper, s->IoMemAddrLower, (unsigned long long)s->IoEip, + s->IoMisc, s->LdtInfo, + s->IoInstructionRestart, + s->AutoHaltRestart); +} + +static int +get_registers(int cpu, struct elf_prstatus *prstatus) +{ + struct sadump_smram_cpu_state smram; + char *prstatus_buf = NULL; + int retval = FALSE, apicid = 0; + + if (!(prstatus_buf = malloc(SIZE(elf_prstatus)))) { + ERRMSG("Can't allocate elf_prstatus buffer. %s\n", + strerror(errno)); + goto error; + } + + if (get_prstatus_from_crash_notes(cpu, prstatus_buf)) { + + if (!copy_regs_from_prstatus(prstatus, prstatus_buf)) + goto cleanup; + + DEBUG_MSG("sadump: cpu #%d registers from crash_notes\n", cpu); + + debug_message_user_regs_struct(cpu, prstatus); + + } else { + + if (!cpu_to_apicid(cpu, &apicid)) + goto cleanup; + + if (!get_smram_cpu_state(apicid, &smram)) + goto cleanup; + + copy_regs_from_smram_cpu_state(prstatus, &smram); + + DEBUG_MSG("sadump: cpu #%d registers from SMRAM\n", cpu); + + debug_message_smram_cpu_state(apicid, &smram); + debug_message_user_regs_struct(cpu, prstatus); + + } + + retval = TRUE; +cleanup: + free(prstatus_buf); +error: + return retval; +} + +int +sadump_add_diskset_info(char *name_memory) +{ + si->num_disks++; + + si->diskset_info = + realloc(si->diskset_info, + si->num_disks*sizeof(struct sadump_diskset_info)); + if (!si->diskset_info) { + ERRMSG("Can't allocate memory for sadump_diskset_info. %s\n", + strerror(errno)); + return FALSE; + } + + si->diskset_info[si->num_disks - 1].name_memory = name_memory; + si->diskset_info[si->num_disks - 1].fd_memory = -1; + + return TRUE; +} + +int +sadump_read_elf_note(char *buf, size_t size_note) +{ + if (!si->file_elf_note) + return FALSE; + + rewind(si->file_elf_note); + + if (fread(buf, size_note, 1, si->file_elf_note) != 1) { + ERRMSG("Can't read elf note file. %s\n", + strerror(errno)); + return FALSE; + } + + return TRUE; +} + +long +sadump_page_size(void) +{ + return si->sh_memory->block_size; +} + +char * +sadump_head_disk_name_memory(void) +{ + return si->diskset_info[0].name_memory; +} + +char * +sadump_format_type_name(void) +{ + switch (info->flag_sadump) { + case SADUMP_SINGLE_PARTITION: + return "single partition"; + case SADUMP_DISKSET: + return "diskset"; + case SADUMP_MEDIA_BACKUP: + return "media backup"; + case SADUMP_UNKNOWN: + return "unknown"; + } + return NULL; +} + +void +free_sadump_info(void) +{ + if (si->sph_memory) + free(si->sph_memory); + if (si->sh_memory) + free(si->sh_memory); + if (si->sdh_memory) + free(si->sdh_memory); + if (si->smh_memory) + free(si->smh_memory); + if (si->diskset_info) { + int i; + + for (i = 1; i < si->num_disks; ++i) { + if (si->diskset_info[i].fd_memory >= 0) + close(si->diskset_info[i].fd_memory); + if (si->diskset_info[i].sph_memory) + free(si->diskset_info[i].sph_memory); + } + free(si->diskset_info); + } + if (si->__per_cpu_offset) + free(si->__per_cpu_offset); + if (si->block_table) + free(si->block_table); + if (si->file_elf_note) + fclose(si->file_elf_note); + if (si->cpu_online_mask_buf) + free(si->cpu_online_mask_buf); + if (si->ram_bitmap) { + if (si->ram_bitmap->buf) + free(si->ram_bitmap->buf); + free(si->ram_bitmap); + } +} + +void +sadump_kdump_backup_region_init(void) +{ + unsigned char buf[BUFSIZE]; + unsigned long i, total, kexec_crash_image_p, elfcorehdr_p; + Elf64_Off e_phoff; + uint16_t e_phnum, e_phentsize; + unsigned long long backup_offset; + unsigned long backup_src_start, backup_src_size; + size_t bufsize; + + if (!readmem(VADDR, SYMBOL(kexec_crash_image), &kexec_crash_image_p, + sizeof(unsigned long))) { + ERRMSG("Can't read kexec_crash_image pointer. %s\n", + strerror(errno)); + return; + } + + if (!kexec_crash_image_p) { + DEBUG_MSG("sadump: kexec crash image was not loaded\n"); + return; + } + + if (!readmem(VADDR, kexec_crash_image_p+OFFSET(kimage.segment), + buf, SIZE(kexec_segment)*ARRAY_LENGTH(kimage.segment))) { + ERRMSG("Can't read kexec_crash_image->segment. %s\n", + strerror(errno)); + return; + } + + elfcorehdr_p = 0; + for (i = 0; i < ARRAY_LENGTH(kimage.segment); ++i) { + char e_ident[EI_NIDENT]; + unsigned mem; + + mem=ULONG(buf+i*SIZE(kexec_segment)+OFFSET(kexec_segment.mem)); + if (!mem) + continue; + + if (!readmem(PADDR, mem, e_ident, SELFMAG)) { + DEBUG_MSG("sadump: failed to read elfcorehdr buffer\n"); + return; + } + + if (strncmp(ELFMAG, e_ident, SELFMAG) == 0) { + elfcorehdr_p = mem; + break; + } + } + if (!elfcorehdr_p) { + DEBUG_MSG("sadump: kexec_crash_image contains no elfcorehdr " + "segment\n"); + return; + } + + if (!readmem(PADDR, elfcorehdr_p, buf, SIZE(elf64_hdr))) { + ERRMSG("Can't read elfcorehdr ELF header. %s\n", + strerror(errno)); + return; + } + + e_phnum = USHORT(buf + OFFSET(elf64_hdr.e_phnum)); + e_phentsize = USHORT(buf + OFFSET(elf64_hdr.e_phentsize)); + e_phoff = ULONG(buf + OFFSET(elf64_hdr.e_phoff)); + + backup_src_start = backup_src_size = backup_offset = 0; + for (i = 0; i < e_phnum; ++i) { + unsigned long p_type, p_offset, p_paddr, p_memsz; + + if (!readmem(PADDR, elfcorehdr_p+e_phoff+i*e_phentsize, buf, + e_phentsize)) { + ERRMSG("Can't read elfcorehdr program header. %s\n", + strerror(errno)); + return; + } + + p_type = UINT(buf + OFFSET(elf64_phdr.p_type)); + p_offset = ULONG(buf + OFFSET(elf64_phdr.p_offset)); + p_paddr = ULONG(buf + OFFSET(elf64_phdr.p_paddr)); + p_memsz = ULONG(buf + OFFSET(elf64_phdr.p_memsz)); + + if (p_type == PT_LOAD && + p_paddr <= KEXEC_BACKUP_SRC_END && + p_paddr + p_memsz <= p_offset) { + + backup_src_start = p_paddr; + backup_src_size = p_memsz; + backup_offset = p_offset; + +DEBUG_MSG("sadump: SRC_START: %#016lx SRC_SIZE: %#016lx SRC_OFFSET: %#016llx\n", + backup_src_start, backup_src_size, backup_offset); + + break; + } + } + if (i == e_phnum) { +DEBUG_MSG("sadump: No PT_LOAD in elfcorehdr for backup area\n"); + return; + } + + bufsize = BUFSIZE; + for (total = 0; total < backup_src_size; total += bufsize) { + + if (backup_src_size - total < BUFSIZE) + bufsize = backup_src_size - total; + + if (!readmem(PADDR, backup_offset + total, buf, bufsize)) { + ERRMSG("Can't read backup region. %s\n", + strerror(errno)); + return; + } + + /* + * We're assuming that the backup region is full of 0 + * before kdump saves the first 640kB memory of the + * 1st kernel in the region. + */ + if (!is_zero_page(buf, bufsize)) { + + si->kdump_backed_up = TRUE; + si->backup_src_start = backup_src_start; + si->backup_src_size = backup_src_size; + si->backup_offset = backup_offset; + + DEBUG_MSG("sadump: kdump backup region used\n"); + + return; + } + } + + DEBUG_MSG("sadump: kdump backup region unused\n"); +} + +#endif /* defined(__x86__) || defined(__x86_64__) */ |