mirror of
https://github.com/crash-utility/crash
synced 2025-02-24 01:16:49 +00:00
calculating phys_base and the mapped kernel offset for KASLR-enabled kernels on SADUMP dumpfiles by using a technique developed by Takao Indoh. Originally, the patchset included support for kdumps, but this was dropped in v2, as it was deemed unnecessary due to the upstream implementation of the "vmcoreinfo device" in QEMU. However, there are still several reasons for which the vmcoreinfo device may not be present at the time when a memory dump is taken from a VM, ranging from a host running older QEMU/libvirt versions, to misconfigured VMs or environments running Hypervisors that doesn't support this device. This patchset generalizes the KASLR-related functions from sadump.c and moves them to kaslr_helper.c, and makes kdump analysis fall back to KASLR offset calculation if vmcoreinfo data is missing. (slp@redhat.com)
1706 lines
44 KiB
C
1706 lines
44 KiB
C
/*
|
|
* sadump.h - core analysis suite
|
|
*
|
|
* Copyright (c) 2011 FUJITSU LIMITED
|
|
*
|
|
* 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.
|
|
*
|
|
* Author: HATAYAMA Daisuke <d.hatayama@jp.fujitsu.com>
|
|
*/
|
|
|
|
#include "defs.h"
|
|
#include "sadump.h"
|
|
#include <arpa/inet.h> /* htonl, htons */
|
|
#include <elf.h>
|
|
#include <inttypes.h>
|
|
|
|
enum {
|
|
failed = -1
|
|
};
|
|
|
|
static struct sadump_data sadump_data = { 0 };
|
|
static struct sadump_data *sd = &sadump_data;
|
|
|
|
static int read_device(void *buf, size_t bytes, ulong *offset);
|
|
static int read_dump_header(char *file);
|
|
static int add_disk(char *file);
|
|
static int open_dump_file(char *file);
|
|
static int open_disk(char *file);
|
|
static uint64_t paddr_to_pfn(physaddr_t paddr);
|
|
static inline int is_set_bit(char *bitmap, uint64_t pfn);
|
|
static inline int page_is_ram(uint64_t nr);
|
|
static inline int page_is_dumpable(uint64_t nr);
|
|
static int lookup_diskset(uint64_t whole_offset, int *diskid, uint64_t *disk_offset);
|
|
static struct tm *efi_time_t_to_tm(const efi_time_t *e);
|
|
static char * guid_to_str(efi_guid_t *guid, char *buf, size_t buflen);
|
|
static int verify_magic_number(uint32_t magicnum[DUMP_PART_HEADER_MAGICNUM_SIZE]);
|
|
static ulong per_cpu_ptr(ulong ptr, int cpu);
|
|
static ulong early_per_cpu_ptr(char *symbol, struct syment *sym, int cpu);
|
|
static ulong legacy_per_cpu_ptr(ulong ptr, int cpu);
|
|
static int get_prstatus_from_crash_notes(int cpu, char *prstatus);
|
|
static void display_smram_cpu_state(int apicid, struct sadump_smram_cpu_state *s);
|
|
static int cpu_to_apicid(int cpu, int *apicid);
|
|
static int get_sadump_smram_cpu_state(int cpu, struct sadump_smram_cpu_state *smram);
|
|
static int block_table_init(void);
|
|
static uint64_t pfn_to_block(uint64_t pfn);
|
|
|
|
struct sadump_data *
|
|
sadump_get_sadump_data(void)
|
|
{
|
|
if (!SADUMP_VALID() || !SADUMP_DUMPFILE())
|
|
return NULL;
|
|
|
|
return &sadump_data;
|
|
}
|
|
|
|
int
|
|
sadump_cleanup_sadump_data(void)
|
|
{
|
|
int i;
|
|
|
|
if (!SADUMP_VALID() || !SADUMP_DUMPFILE())
|
|
return FALSE;
|
|
|
|
if (sd->flags & SADUMP_DISKSET) {
|
|
for (i = 1; i < sd->sd_list_len; ++i) {
|
|
if (sd->sd_list[i]->dfd)
|
|
close(sd->sd_list[i]->dfd);
|
|
free(sd->sd_list[i]->header);
|
|
free(sd->sd_list[i]);
|
|
}
|
|
}
|
|
|
|
close(sd->dfd);
|
|
free(sd->header);
|
|
free(sd->dump_header);
|
|
free(sd->diskset_header);
|
|
free(sd->bitmap);
|
|
free(sd->dumpable_bitmap);
|
|
free(sd->page_buf);
|
|
free(sd->block_table);
|
|
if (sd->sd_list[0])
|
|
free(sd->sd_list[0]);
|
|
free(sd->sd_list);
|
|
|
|
memset(&sadump_data, 0, sizeof(sadump_data));
|
|
|
|
pc->flags &= ~SADUMP;
|
|
pc->dumpfile = NULL;
|
|
pc->readmem = NULL;
|
|
pc->writemem = NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static int
|
|
read_device(void *buf, size_t bytes, ulong *offset)
|
|
{
|
|
if (lseek(sd->dfd, *offset, SEEK_SET) == failed) {
|
|
error(INFO, "sadump: cannot lseek dump device\n");
|
|
return FALSE;
|
|
}
|
|
if (read(sd->dfd, buf, bytes) < bytes) {
|
|
error(INFO, "sadump: cannot read dump device\n");
|
|
return FALSE;
|
|
}
|
|
*offset += bytes;
|
|
return TRUE;
|
|
}
|
|
|
|
static int
|
|
read_dump_header(char *file)
|
|
{
|
|
struct sadump_part_header *sph = NULL;
|
|
struct sadump_header *sh = NULL;
|
|
struct sadump_disk_set_header *new, *sdh = NULL;
|
|
struct sadump_media_header *smh = NULL;
|
|
struct sadump_diskset_data *sd_list_len_0 = NULL;
|
|
size_t block_size = SADUMP_DEFAULT_BLOCK_SIZE;
|
|
ulong flags = 0;
|
|
ulong offset = 0, sub_hdr_offset, data_offset;
|
|
uint32_t smram_cpu_state_size = 0;
|
|
ulong bitmap_len, dumpable_bitmap_len;
|
|
char *bitmap = NULL, *dumpable_bitmap = NULL, *page_buf = NULL;
|
|
char guid1[SADUMP_EFI_GUID_TEXT_REPR_LEN+1];
|
|
char guid2[SADUMP_EFI_GUID_TEXT_REPR_LEN+1];
|
|
|
|
sph = malloc(block_size);
|
|
if (!sph) {
|
|
error(INFO, "sadump: cannot allocate partition header buffer\n");
|
|
goto err;
|
|
}
|
|
|
|
sdh = malloc(block_size);
|
|
if (!sdh) {
|
|
error(INFO, "sadump: cannot allocate disk set header buffer\n");
|
|
goto err;
|
|
}
|
|
|
|
sh = malloc(block_size);
|
|
if (!sh) {
|
|
error(INFO, "sadump: cannot allocate dump header buffer\n");
|
|
goto err;
|
|
}
|
|
|
|
smh = malloc(block_size);
|
|
if (!smh) {
|
|
error(INFO, "sadump: cannot allocate media header buffer\n");
|
|
goto err;
|
|
}
|
|
|
|
restart:
|
|
if (!read_device(sph, block_size, &offset)) {
|
|
error(INFO, "sadump: cannot read partition header\n");
|
|
goto err;
|
|
}
|
|
|
|
if (sph->signature1 != SADUMP_SIGNATURE1 ||
|
|
sph->signature2 != SADUMP_SIGNATURE2) {
|
|
|
|
flags |= SADUMP_MEDIA;
|
|
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "sadump: read dump device as media "
|
|
"format\n");
|
|
|
|
offset = 0;
|
|
|
|
if (!read_device(smh, block_size, &offset)) {
|
|
error(INFO, "sadump: cannot read media header\n");
|
|
goto err;
|
|
}
|
|
|
|
if (!read_device(sph, block_size, &offset)) {
|
|
error(INFO, "sadump: cannot read partition header\n");
|
|
goto err;
|
|
}
|
|
|
|
if (sph->signature1 != SADUMP_SIGNATURE1 ||
|
|
sph->signature2 != SADUMP_SIGNATURE2) {
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "sadump: does not have partition "
|
|
"header\n");
|
|
goto err;
|
|
}
|
|
|
|
}
|
|
|
|
if (!verify_magic_number(sph->magicnum)) {
|
|
error(INFO, "sadump: invalid magic number\n");
|
|
goto err;
|
|
}
|
|
|
|
if (!(flags & SADUMP_MEDIA) && sph->set_disk_set) {
|
|
uint32_t header_blocks;
|
|
size_t header_size;
|
|
|
|
flags |= SADUMP_DISKSET;
|
|
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "sadump: read dump device as diskset\n");
|
|
|
|
if (sph->set_disk_set != 1 ||
|
|
sph->set_disk_set > SADUMP_MAX_DISK_SET_NUM) {
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "sadump: invalid disk set number: "
|
|
"%d\n",
|
|
sph->set_disk_set);
|
|
goto err;
|
|
}
|
|
|
|
if (!read_device(&header_blocks, sizeof(uint32_t), &offset)) {
|
|
error(INFO, "sadump: cannot read disk set header "
|
|
"size\n");
|
|
goto err;
|
|
}
|
|
|
|
offset -= sizeof(uint32_t);
|
|
header_size = header_blocks * block_size;
|
|
|
|
if (header_size > block_size) {
|
|
new = realloc(sdh, header_size);
|
|
if (!new) {
|
|
error(INFO, "sadump: cannot re-allocate disk "
|
|
"set buffer\n");
|
|
goto err;
|
|
}
|
|
sdh = new;
|
|
}
|
|
|
|
if (!read_device(sdh, header_size, &offset)) {
|
|
error(INFO, "sadump: cannot read disk set header\n");
|
|
goto err;
|
|
}
|
|
|
|
}
|
|
|
|
if (!read_device(sh, block_size, &offset)) {
|
|
error(INFO, "sadump: cannot read dump header\n");
|
|
goto err;
|
|
}
|
|
|
|
sub_hdr_offset = offset;
|
|
|
|
if (strncmp(sh->signature, SADUMP_SIGNATURE, 8) != 0) {
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "sadump: does not have dump header\n");
|
|
goto err;
|
|
}
|
|
|
|
if (flags & SADUMP_MEDIA) {
|
|
|
|
if (memcmp(&sph->sadump_id, &smh->sadump_id,
|
|
sizeof(efi_guid_t)) != 0) {
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "sadump: system ID mismatch\n"
|
|
" partition header: %s\n"
|
|
" media header: %s\n",
|
|
guid_to_str(&sph->sadump_id, guid1, sizeof(guid1)),
|
|
guid_to_str(&smh->sadump_id, guid2, sizeof(guid2)));
|
|
goto err;
|
|
}
|
|
|
|
if (memcmp(&sph->disk_set_id, &smh->disk_set_id,
|
|
sizeof(efi_guid_t)) != 0) {
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "sadump: disk set ID mismatch\n"
|
|
" partition header: %s\n"
|
|
" media header: %s\n",
|
|
guid_to_str(&sph->disk_set_id, guid1, sizeof(guid1)),
|
|
guid_to_str(&smh->disk_set_id, guid2, sizeof(guid2)));
|
|
goto err;
|
|
}
|
|
|
|
if (memcmp(&sph->time_stamp, &smh->time_stamp,
|
|
sizeof(efi_time_t)) != 0) {
|
|
if (CRASHDEBUG(1)) {
|
|
error(INFO, "sadump: time stamp mismatch\n");
|
|
error(INFO, "sadump: partition header: %s\n",
|
|
strip_linefeeds(asctime
|
|
(efi_time_t_to_tm
|
|
(&sph->time_stamp))));
|
|
error(INFO, "sadump: media header: %s\n",
|
|
strip_linefeeds(asctime
|
|
(efi_time_t_to_tm
|
|
(&smh->time_stamp))));
|
|
}
|
|
}
|
|
|
|
if (smh->sequential_num != 1) {
|
|
error(INFO, "sadump: first media file has sequential "
|
|
"number %d\n", smh->sequential_num);
|
|
goto err;
|
|
}
|
|
|
|
}
|
|
|
|
if (sh->block_size != block_size) {
|
|
block_size = sh->block_size;
|
|
offset = 0;
|
|
goto restart;
|
|
}
|
|
|
|
if (CRASHDEBUG(1)) {
|
|
if (flags & SADUMP_MEDIA)
|
|
error(INFO, "sadump: media backup file\n");
|
|
|
|
else if (flags & SADUMP_DISKSET)
|
|
error(INFO, "sadump: diskset configuration with %d "
|
|
"disks\n", sdh->disk_num);
|
|
|
|
else
|
|
error(INFO, "sadump: single partition "
|
|
"configuration\n");
|
|
}
|
|
|
|
flags |= SADUMP_LOCAL;
|
|
|
|
switch (sh->header_version) {
|
|
case 0:
|
|
sd->max_mapnr = (uint64_t)sh->max_mapnr;
|
|
break;
|
|
default:
|
|
error(WARNING,
|
|
"sadump: unsupported header version: %u\n"
|
|
"sadump: assuming header version: 1\n",
|
|
sh->header_version);
|
|
case 1:
|
|
sd->max_mapnr = sh->max_mapnr_64;
|
|
break;
|
|
}
|
|
|
|
if (sh->sub_hdr_size > 0) {
|
|
if (!read_device(&smram_cpu_state_size, sizeof(uint32_t),
|
|
&offset)) {
|
|
error(INFO,
|
|
"sadump: cannot read SMRAM CPU STATE size\n");
|
|
goto err;
|
|
}
|
|
smram_cpu_state_size /= sh->nr_cpus;
|
|
|
|
offset -= sizeof(uint32_t);
|
|
offset += sh->sub_hdr_size * block_size;
|
|
}
|
|
|
|
if (!sh->bitmap_blocks) {
|
|
error(INFO, "sadump: bitmap_blocks is zero\n");
|
|
goto err;
|
|
}
|
|
bitmap_len = block_size * sh->bitmap_blocks;
|
|
bitmap = calloc(bitmap_len, 1);
|
|
if (!bitmap) {
|
|
error(INFO, "sadump: cannot allocate memory for bitmap "
|
|
"buffer\n");
|
|
goto err;
|
|
}
|
|
if (!read_device(bitmap, bitmap_len, &offset)) {
|
|
error(INFO, "sadump: cannot read bitmap\n");
|
|
goto err;
|
|
}
|
|
|
|
if (!sh->dumpable_bitmap_blocks) {
|
|
error(INFO, "sadump: dumpable_bitmap_blocks is zero\n");
|
|
goto err;
|
|
}
|
|
dumpable_bitmap_len = block_size * sh->dumpable_bitmap_blocks;
|
|
dumpable_bitmap = calloc(dumpable_bitmap_len, 1);
|
|
if (!dumpable_bitmap) {
|
|
error(INFO, "sadump: cannot allocate memory for "
|
|
"dumpable_bitmap buffer\n");
|
|
goto err;
|
|
}
|
|
if (!read_device(dumpable_bitmap, dumpable_bitmap_len, &offset)) {
|
|
error(INFO, "sadump: cannot read dumpable bitmap\n");
|
|
goto err;
|
|
}
|
|
|
|
data_offset = offset;
|
|
|
|
page_buf = malloc(block_size);
|
|
if (!page_buf) {
|
|
error(INFO, "sadump: cannot allocate page buffer\n");
|
|
goto err;
|
|
}
|
|
|
|
sd->filename = file;
|
|
|
|
/*
|
|
* Switch to zero excluded mode by default on sadump-related
|
|
* formats because some Fujitsu troubleshooting software
|
|
* assumes the behavior.
|
|
*/
|
|
sd->flags = flags | SADUMP_ZERO_EXCLUDED;
|
|
|
|
if (machine_type("X86"))
|
|
sd->machine_type = EM_386;
|
|
else if (machine_type("X86_64"))
|
|
sd->machine_type = EM_X86_64;
|
|
else {
|
|
error(INFO, "sadump: unsupported machine type: %s\n",
|
|
MACHINE_TYPE);
|
|
goto err;
|
|
}
|
|
|
|
sd->data_offset = data_offset;
|
|
sd->block_size = block_size;
|
|
sd->block_shift = ffs(sd->block_size) - 1;
|
|
|
|
sd->bitmap = bitmap;
|
|
sd->dumpable_bitmap = dumpable_bitmap;
|
|
|
|
sd->sub_hdr_offset = sub_hdr_offset;
|
|
sd->smram_cpu_state_size = smram_cpu_state_size;
|
|
|
|
sd->header = sph;
|
|
sd->dump_header = sh;
|
|
if (flags & SADUMP_DISKSET)
|
|
sd->diskset_header = sdh;
|
|
if (flags & SADUMP_MEDIA)
|
|
sd->media_header = smh;
|
|
|
|
sd->page_buf = page_buf;
|
|
|
|
if (flags & SADUMP_DISKSET) {
|
|
|
|
sd_list_len_0 = malloc(sizeof(struct sadump_diskset_data));
|
|
if (!sd_list_len_0) {
|
|
error(INFO,
|
|
"sadump: cannot allocate diskset data buffer\n");
|
|
goto err;
|
|
}
|
|
|
|
sd_list_len_0->filename = sd->filename;
|
|
sd_list_len_0->dfd = sd->dfd;
|
|
sd_list_len_0->header = sd->header;
|
|
sd_list_len_0->data_offset = sd->data_offset;
|
|
|
|
sd->sd_list = malloc(sizeof(struct sadump_diskset_data *));
|
|
if (!sd->sd_list) {
|
|
error(INFO,
|
|
"sadump: cannot allocate diskset list buffer\n");
|
|
goto err;
|
|
}
|
|
|
|
sd->sd_list_len = 1;
|
|
sd->sd_list[0] = sd_list_len_0;
|
|
}
|
|
|
|
if (!block_table_init()) {
|
|
error(INFO, "sadump: cannot initialize block hash table\n");
|
|
goto err;
|
|
}
|
|
|
|
if (!(flags & SADUMP_DISKSET))
|
|
free(sdh);
|
|
|
|
if (!(flags & SADUMP_MEDIA))
|
|
free(smh);
|
|
|
|
return TRUE;
|
|
|
|
err:
|
|
close(sd->dfd);
|
|
|
|
free(sph);
|
|
free(sdh);
|
|
free(sh);
|
|
free(smh);
|
|
free(bitmap);
|
|
free(dumpable_bitmap);
|
|
free(page_buf);
|
|
free(sd_list_len_0);
|
|
|
|
free(sd->sd_list);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static int
|
|
add_disk(char *file)
|
|
{
|
|
struct sadump_part_header *ph;
|
|
struct sadump_diskset_data *this_disk;
|
|
int diskid;
|
|
char guid1[SADUMP_EFI_GUID_TEXT_REPR_LEN+1];
|
|
char guid2[SADUMP_EFI_GUID_TEXT_REPR_LEN+1];
|
|
|
|
diskid = sd->sd_list_len - 1;
|
|
this_disk = sd->sd_list[diskid];
|
|
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "sadump: add disk #%d\n", diskid+1);
|
|
|
|
ph = malloc(sd->block_size);
|
|
if (!ph) {
|
|
error(INFO, "sadump: cannot malloc block_size buffer\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if (lseek(this_disk->dfd, 0, SEEK_SET) == failed) {
|
|
error(INFO, "sadump: cannot lseek dump partition header\n");
|
|
free(ph);
|
|
return FALSE;
|
|
}
|
|
if (read(this_disk->dfd, ph, sd->block_size) < sd->block_size) {
|
|
error(INFO, "sadump: cannot read dump partition header\n");
|
|
free(ph);
|
|
return FALSE;
|
|
}
|
|
|
|
if (ph->signature1 != SADUMP_SIGNATURE1 ||
|
|
ph->signature2 != SADUMP_SIGNATURE2) {
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "sadump: does not have partition header\n");
|
|
free(ph);
|
|
return FALSE;
|
|
}
|
|
|
|
if (memcmp(&sd->header->sadump_id, &ph->sadump_id,
|
|
sizeof(efi_guid_t)) != 0) {
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "sadump: system ID mismatch\n"
|
|
" partition header on disk #1: %s\n"
|
|
" partition header on disk #%d: %s\n",
|
|
guid_to_str(&sd->header->sadump_id, guid1,
|
|
sizeof(guid1)),
|
|
diskid+1,
|
|
guid_to_str(&ph->sadump_id, guid2,
|
|
sizeof(guid2)));
|
|
free(ph);
|
|
return FALSE;
|
|
}
|
|
|
|
if (memcmp(&sd->header->disk_set_id, &ph->disk_set_id, sizeof(efi_guid_t)) != 0) {
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "sadump: disk set ID mismatch\n"
|
|
" partition header on disk #1: %s\n"
|
|
" partition header on disk #%d: %s\n",
|
|
guid_to_str(&sd->header->disk_set_id, guid1,
|
|
sizeof(guid1)),
|
|
diskid+1,
|
|
guid_to_str(&ph->disk_set_id, guid2,
|
|
sizeof(guid2)));
|
|
free(ph);
|
|
return FALSE;
|
|
}
|
|
|
|
if (memcmp(&sd->diskset_header->vol_info[diskid - 1].id, &ph->vol_id,
|
|
sizeof(efi_guid_t)) != 0) {
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "sadump: volume ID mismatch\n"
|
|
" disk set header on disk #1: %s\n"
|
|
" partition header on disk #%d: %s\n",
|
|
guid_to_str(&sd->diskset_header->vol_info[diskid-1].id,
|
|
guid1, sizeof(guid1)),
|
|
diskid+1,
|
|
guid_to_str(&ph->vol_id, guid2, sizeof(guid2)));
|
|
free(ph);
|
|
return FALSE;
|
|
}
|
|
|
|
if (memcmp(&sd->header->time_stamp, &ph->time_stamp,
|
|
sizeof(efi_time_t)) != 0) {
|
|
if (CRASHDEBUG(1)) {
|
|
error(INFO, "sadump: time stamp mismatch\n");
|
|
error(INFO,
|
|
"sadump: partition header on disk #1: %s\n",
|
|
strip_linefeeds(asctime
|
|
(efi_time_t_to_tm
|
|
(&sd->header->time_stamp))));
|
|
error(INFO,
|
|
"sadump: partition header on disk #%d: %s\n",
|
|
diskid+1,
|
|
strip_linefeeds(asctime
|
|
(efi_time_t_to_tm
|
|
(&ph->time_stamp))));
|
|
}
|
|
}
|
|
|
|
if (diskid != ph->set_disk_set - 1) {
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "sadump: wrong disk order; "
|
|
"#%d expected but #%d given\n",
|
|
diskid+1, ph->set_disk_set);
|
|
free(ph);
|
|
return FALSE;
|
|
}
|
|
|
|
this_disk->header = ph;
|
|
this_disk->data_offset = sd->block_size;
|
|
this_disk->filename = file;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static int
|
|
open_dump_file(char *file)
|
|
{
|
|
int fd;
|
|
|
|
fd = open(file, O_RDONLY);
|
|
if (fd < 0) {
|
|
error(INFO, "sadump: unable to open dump file %s", file);
|
|
return FALSE;
|
|
}
|
|
|
|
sd->dfd = fd;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static int
|
|
open_disk(char *file)
|
|
{
|
|
struct sadump_diskset_data *this_disk;
|
|
|
|
sd->sd_list_len++;
|
|
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "sadump: open disk #%d\n", sd->sd_list_len);
|
|
|
|
if (sd->sd_list_len > sd->diskset_header->disk_num) {
|
|
error(INFO, "sadump: too many diskset arguments; "
|
|
"this diskset consists of %d disks\n",
|
|
sd->diskset_header->disk_num);
|
|
return FALSE;
|
|
}
|
|
|
|
sd->sd_list = realloc(sd->sd_list,
|
|
sd->sd_list_len *
|
|
sizeof(struct sadump_diskset_data *));
|
|
if (!sd->sd_list) {
|
|
if (CRASHDEBUG(1)) {
|
|
error(INFO, "sadump: cannot malloc diskset list buffer\n");
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
this_disk = malloc(sizeof(struct sadump_diskset_data));
|
|
if (!this_disk) {
|
|
if (CRASHDEBUG(1)) {
|
|
error(INFO, "sadump: cannot malloc diskset data buffer\n");
|
|
}
|
|
return FALSE;
|
|
}
|
|
memset(this_disk, 0, sizeof(*this_disk));
|
|
sd->sd_list[sd->sd_list_len - 1] = this_disk;
|
|
|
|
this_disk->dfd = open(file, O_RDONLY);
|
|
if (!this_disk->dfd) {
|
|
free(this_disk);
|
|
error(INFO, "sadump: unable to open dump file %s", file);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int is_sadump(char *file)
|
|
{
|
|
if (SADUMP_VALID()) {
|
|
|
|
if (!(sd->flags & SADUMP_DISKSET)) {
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "sadump: does not support multiple"
|
|
" file formats\n");
|
|
(void) sadump_cleanup_sadump_data();
|
|
return FALSE;
|
|
}
|
|
|
|
if (!open_disk(file) || !add_disk(file)) {
|
|
(void) sadump_cleanup_sadump_data();
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if (!open_dump_file(file) || !read_dump_header(file))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int sadump_is_diskset(void)
|
|
{
|
|
if (!SADUMP_VALID())
|
|
return FALSE;
|
|
|
|
return !!(sd->flags & SADUMP_DISKSET);
|
|
}
|
|
|
|
uint sadump_page_size(void)
|
|
{
|
|
return sd->dump_header->block_size;
|
|
}
|
|
|
|
/*
|
|
* Translate physical address in paddr to PFN number. This means normally that
|
|
* we just shift paddr by some constant.
|
|
*/
|
|
static uint64_t
|
|
paddr_to_pfn(physaddr_t paddr)
|
|
{
|
|
return paddr >> sd->block_shift;
|
|
}
|
|
|
|
static inline int
|
|
is_set_bit(char *bitmap, uint64_t pfn)
|
|
{
|
|
ulong index, bit;
|
|
|
|
index = pfn >> 3;
|
|
bit = 7 - (pfn & 7);
|
|
|
|
return !!(bitmap[index] & (1UL << bit));
|
|
}
|
|
|
|
static inline int
|
|
page_is_ram(uint64_t nr)
|
|
{
|
|
return is_set_bit(sd->bitmap, nr);
|
|
}
|
|
|
|
static inline int
|
|
page_is_dumpable(uint64_t nr)
|
|
{
|
|
return is_set_bit(sd->dumpable_bitmap, nr);
|
|
}
|
|
|
|
static int
|
|
lookup_diskset(uint64_t whole_offset, int *diskid, uint64_t *disk_offset)
|
|
{
|
|
uint64_t offset = whole_offset;
|
|
int i;
|
|
|
|
for (i = 0; i < sd->sd_list_len; ++i) {
|
|
uint64_t used_device_i, ram_size;
|
|
ulong data_offset_i;
|
|
|
|
used_device_i = sd->sd_list[i]->header->used_device;
|
|
data_offset_i = sd->sd_list[i]->data_offset;
|
|
|
|
ram_size = used_device_i - data_offset_i;
|
|
|
|
if (offset < ram_size)
|
|
break;
|
|
offset -= ram_size;
|
|
}
|
|
|
|
if (i == sd->sd_list_len)
|
|
return FALSE;
|
|
|
|
*diskid = i;
|
|
*disk_offset = offset;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int read_sadump(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr)
|
|
{
|
|
physaddr_t curpaddr ATTRIBUTE_UNUSED;
|
|
uint64_t pfn, whole_offset, perdisk_offset, block;
|
|
ulong page_offset;
|
|
int dfd;
|
|
|
|
if (sd->flags & SADUMP_KDUMP_BACKUP &&
|
|
paddr >= sd->backup_src_start &&
|
|
paddr < sd->backup_src_start + sd->backup_src_size) {
|
|
ulong orig_paddr;
|
|
|
|
orig_paddr = paddr;
|
|
paddr += sd->backup_offset - sd->backup_src_start;
|
|
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "sadump: kdump backup region: %#llx => %#llx\n",
|
|
orig_paddr, paddr);
|
|
|
|
}
|
|
|
|
pfn = paddr_to_pfn(paddr);
|
|
|
|
curpaddr = paddr & ~((physaddr_t)(sd->block_size-1));
|
|
page_offset = paddr & ((physaddr_t)(sd->block_size-1));
|
|
|
|
if ((pfn >= sd->max_mapnr) || !page_is_ram(pfn))
|
|
return SEEK_ERROR;
|
|
if (!page_is_dumpable(pfn)) {
|
|
if (!(sd->flags & SADUMP_ZERO_EXCLUDED))
|
|
return PAGE_EXCLUDED;
|
|
memset(bufptr, 0, cnt);
|
|
return cnt;
|
|
}
|
|
|
|
block = pfn_to_block(pfn);
|
|
|
|
whole_offset = block * sd->block_size;
|
|
|
|
if (sd->flags & SADUMP_DISKSET) {
|
|
int diskid;
|
|
|
|
if (!lookup_diskset(whole_offset, &diskid, &perdisk_offset))
|
|
return SEEK_ERROR;
|
|
|
|
dfd = sd->sd_list[diskid]->dfd;
|
|
perdisk_offset += sd->sd_list[diskid]->data_offset;
|
|
|
|
} else {
|
|
dfd = sd->dfd;
|
|
perdisk_offset = whole_offset + sd->data_offset;
|
|
|
|
}
|
|
|
|
if (lseek(dfd, perdisk_offset, SEEK_SET) == failed)
|
|
return SEEK_ERROR;
|
|
|
|
if (read(dfd, sd->page_buf, sd->block_size) != sd->block_size)
|
|
return READ_ERROR;
|
|
|
|
memcpy(bufptr, sd->page_buf + page_offset, cnt);
|
|
|
|
return cnt;
|
|
}
|
|
|
|
int write_sadump(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int sadump_init(char *unused, FILE *fptr)
|
|
{
|
|
if (!SADUMP_VALID())
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
ulong get_sadump_panic_task(void)
|
|
{
|
|
return NO_TASK;
|
|
}
|
|
|
|
ulong get_sadump_switch_stack(ulong task)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
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 if (CRASHDEBUG(1))
|
|
error(INFO, "sadump: timezone information is missing\n");
|
|
|
|
ti = mktime(&t);
|
|
if (ti == (time_t)-1)
|
|
return &t;
|
|
|
|
return localtime_r(&ti, &t);
|
|
}
|
|
|
|
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 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;
|
|
}
|
|
|
|
int sadump_memory_used(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int sadump_free_memory(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* This function is dump-type independent, and could be used to dump
|
|
* the diskdump_data structure contents and perhaps the sadump header
|
|
* data.
|
|
*/
|
|
int sadump_memory_dump(FILE *fp)
|
|
{
|
|
struct sadump_part_header *sph;
|
|
struct sadump_disk_set_header *sdh;
|
|
struct sadump_header *sh;
|
|
struct sadump_media_header *smh;
|
|
int i, others;
|
|
char guid[SADUMP_EFI_GUID_TEXT_REPR_LEN+1];
|
|
|
|
fprintf(fp, "sadump_data: \n");
|
|
fprintf(fp, " filename: %s\n", sd->filename);
|
|
fprintf(fp, " flags: %lx (", sd->flags);
|
|
others = 0;
|
|
if (sd->flags & SADUMP_LOCAL)
|
|
fprintf(fp, "%sSADUMP_LOCAL", others++ ? "|" : "");
|
|
if (sd->flags & SADUMP_DISKSET)
|
|
fprintf(fp, "%sSADUMP_DISKSET", others++ ? "|" : "");
|
|
if (sd->flags & SADUMP_MEDIA)
|
|
fprintf(fp, "%sSADUMP_MEDIA", others++ ? "|" : "");
|
|
if (sd->flags & SADUMP_ZERO_EXCLUDED)
|
|
fprintf(fp, "%sSADUMP_ZERO_EXCLUDED", others++ ? "|" : "");
|
|
if (sd->flags & SADUMP_KDUMP_BACKUP)
|
|
fprintf(fp, "%sSADUMP_KDUMP_BACKUP", others++ ? "|" : "");
|
|
fprintf(fp, ") \n");
|
|
fprintf(fp, " dfd: %d\n", sd->dfd);
|
|
fprintf(fp, " machine_type: %d ", sd->machine_type);
|
|
switch (sd->machine_type)
|
|
{
|
|
case EM_386:
|
|
fprintf(fp, "(EM_386)\n"); break;
|
|
case EM_X86_64:
|
|
fprintf(fp, "(EM_X86_64)\n"); break;
|
|
default:
|
|
fprintf(fp, "(unknown)\n"); break;
|
|
}
|
|
|
|
fprintf(fp, "\n header: %lx\n", (ulong)sd->header);
|
|
sph = sd->header;
|
|
fprintf(fp, " signature1: %x\n", sph->signature1);
|
|
fprintf(fp, " signature2: %x\n", sph->signature2);
|
|
fprintf(fp, " enable: %u\n", sph->enable);
|
|
fprintf(fp, " reboot: %u\n", sph->reboot);
|
|
fprintf(fp, " compress: %u\n", sph->compress);
|
|
fprintf(fp, " recycle: %u\n", sph->recycle);
|
|
fprintf(fp, " label: (unused)\n");
|
|
fprintf(fp, " sadump_id: %s\n", guid_to_str(&sph->sadump_id, guid, sizeof(guid)));
|
|
fprintf(fp, " disk_set_id: %s\n", guid_to_str(&sph->disk_set_id, guid, sizeof(guid)));
|
|
fprintf(fp, " vol_id: %s\n", guid_to_str(&sph->vol_id, guid, sizeof(guid)));
|
|
fprintf(fp, " time_stamp: %s\n",
|
|
strip_linefeeds(asctime(efi_time_t_to_tm(&sph->time_stamp))));
|
|
fprintf(fp, " set_disk_set: %u\n", sph->set_disk_set);
|
|
fprintf(fp, " reserve: %u\n", sph->reserve);
|
|
fprintf(fp, " used_device: %llu\n", (ulonglong)sph->used_device);
|
|
fprintf(fp, " magicnum: %s\n",
|
|
verify_magic_number(sph->magicnum)
|
|
? "(valid)" : "(invalid)");
|
|
|
|
fprintf(fp, "\n dump header: %lx\n", (ulong)sd->dump_header);
|
|
sh = sd->dump_header;
|
|
fprintf(fp, " signature: %s\n", sh->signature);
|
|
fprintf(fp, " header_version: %u\n", sh->header_version);
|
|
fprintf(fp, " reserve: %u\n", sh->reserve);
|
|
fprintf(fp, " timestamp: %s\n",
|
|
strip_linefeeds(asctime(efi_time_t_to_tm(&sh->timestamp))));
|
|
fprintf(fp, " status: %u\n", sh->status);
|
|
fprintf(fp, " compress: %u\n", sh->compress);
|
|
fprintf(fp, " block_size: %u\n", sh->block_size);
|
|
fprintf(fp, " extra_hdr_size: %u\n", sh->extra_hdr_size);
|
|
fprintf(fp, " sub_hdr_size: %u\n", sh->sub_hdr_size);
|
|
fprintf(fp, " bitmap_blocks: %u\n", sh->bitmap_blocks);
|
|
fprintf(fp, "dumpable_bitmap_blocks: %u\n", sh->dumpable_bitmap_blocks);
|
|
fprintf(fp, " max_mapnr: %u\n", sh->max_mapnr);
|
|
fprintf(fp, " total_ram_blocks: %u\n", sh->total_ram_blocks);
|
|
fprintf(fp, " device_blocks: %u\n", sh->device_blocks);
|
|
fprintf(fp, " written_blocks: %u\n", sh->written_blocks);
|
|
fprintf(fp, " current_cpu: %u\n", sh->current_cpu);
|
|
fprintf(fp, " nr_cpus: %u\n", sh->nr_cpus);
|
|
if (sh->header_version >= 1) {
|
|
fprintf(fp,
|
|
" max_mapnr_64: %" PRIu64 "\n"
|
|
" total_ram_blocks_64: %" PRIu64 "\n"
|
|
" device_blocks_64: %" PRIu64 "\n"
|
|
" written_blocks_64: %" PRIu64 "\n",
|
|
sh->max_mapnr_64,
|
|
sh->total_ram_blocks_64,
|
|
sh->device_blocks_64,
|
|
sh->written_blocks_64);
|
|
}
|
|
|
|
fprintf(fp, "\n dump sub heaer: ");
|
|
if (sh->sub_hdr_size > 0) {
|
|
ulong offset = sd->sub_hdr_offset;
|
|
struct sadump_apic_state as;
|
|
struct sadump_smram_cpu_state scs, zero;
|
|
uint32_t size;
|
|
uint aid;
|
|
|
|
memset(&zero, 0, sizeof(zero));
|
|
|
|
if (!read_device(&size, sizeof(uint32_t), &offset)) {
|
|
error(INFO, "sadump: cannot read sub header size\n");
|
|
return FALSE;
|
|
}
|
|
fprintf(fp, "\n size: %u\n", size);
|
|
for (aid = 0; aid < sh->nr_cpus; ++aid) {
|
|
if (!read_device(&as, sizeof(as), &offset)) {
|
|
error(INFO, "sadump: cannot read sub header "
|
|
"apic_id\n");
|
|
return FALSE;
|
|
}
|
|
fprintf(fp, " "
|
|
"apic_id[%u]: ApicId %llu: Ldr: %llu\n",
|
|
aid, (ulonglong)as.ApicId, (ulonglong)as.Ldr);
|
|
}
|
|
for (aid = 0; aid < sh->nr_cpus; ++aid) {
|
|
if (!read_device(&scs, sizeof(scs), &offset)) {
|
|
error(INFO, "sadump: cannot read sub header "
|
|
"cpu_state\n");
|
|
return FALSE;
|
|
}
|
|
if (memcmp(&scs, &zero, sizeof(scs)) != 0) {
|
|
fprintf(fp, "\n");
|
|
display_smram_cpu_state(aid, &scs);
|
|
}
|
|
}
|
|
} else
|
|
fprintf(fp, "(n/a)\n");
|
|
|
|
fprintf(fp, "\n disk set header: %lx ", (ulong)sd->diskset_header);
|
|
if ((sdh = sd->diskset_header)) {
|
|
fprintf(fp, "\ndisk_set_header_size: %u\n", sdh->disk_set_header_size);
|
|
fprintf(fp, " disk_num: %u\n", sdh->disk_num);
|
|
fprintf(fp, " disk_set_size: %llu\n", (ulonglong)sdh->disk_set_size);
|
|
for (i = 0; i < sdh->disk_num - 1; ++i) {
|
|
struct sadump_volume_info *vol = &sdh->vol_info[i];
|
|
|
|
fprintf(fp, " vol_info[%d]: \n", i);
|
|
fprintf(fp, " id: %s\n", guid_to_str(&vol->id, guid, sizeof(guid)));
|
|
fprintf(fp, " vol_size: %llu\n", (ulonglong)vol->vol_size);
|
|
fprintf(fp, " status: %u\n", vol->status);
|
|
fprintf(fp, " cache_size: %u\n", vol->cache_size);
|
|
}
|
|
} else
|
|
fprintf(fp, "(n/a)\n");
|
|
|
|
fprintf(fp, "\n media header: %lx ", (ulong)sd->media_header);
|
|
if ((smh = sd->media_header)) {
|
|
fprintf(fp, "\n sadump_id: %s\n", guid_to_str(&smh->sadump_id, guid, sizeof(guid)));
|
|
fprintf(fp, " disk_set_id: %s\n", guid_to_str(&smh->disk_set_id, guid, sizeof(guid)));
|
|
fprintf(fp, " time_stamp: %s\n",
|
|
strip_linefeeds(asctime(efi_time_t_to_tm(&smh->time_stamp))));
|
|
fprintf(fp, " sequential_num: %d\n", smh->sequential_num);
|
|
fprintf(fp, " term_cord: %d\n", smh->term_cord);
|
|
fprintf(fp, "disk_set_header_size: %d\n", smh->disk_set_header_size);
|
|
fprintf(fp, " disks_in_use: %d\n", smh->disks_in_use);
|
|
fprintf(fp, " reserve: (not displayed) \n");
|
|
} else
|
|
fprintf(fp, "(n/a)\n");
|
|
|
|
fprintf(fp, "\n bitmap: %lx\n", (ulong)sd->bitmap);
|
|
fprintf(fp, " dumpable_bitmap: %lx\n", (ulong)sd->dumpable_bitmap);
|
|
fprintf(fp, " sub_hdr_offset: %lx\n", (ulong)sd->sub_hdr_offset);
|
|
fprintf(fp, "smram_cpu_state_size: %lx\n", (ulong)sd->smram_cpu_state_size);
|
|
fprintf(fp, " data_offset: %lx\n", sd->data_offset);
|
|
fprintf(fp, " block_size: %d\n", sd->block_size);
|
|
fprintf(fp, " block_shift: %d\n", sd->block_shift);
|
|
fprintf(fp, " page_buf: %lx\n", (ulong)sd->page_buf);
|
|
fprintf(fp, " block_table: %lx\n", (ulong)sd->block_table);
|
|
fprintf(fp, " sd_list_len: %d\n", sd->sd_list_len);
|
|
fprintf(fp, " sd_list: %lx\n", (ulong)sd->sd_list);
|
|
fprintf(fp, " backup_src_start: %llx\n", sd->backup_src_start);
|
|
fprintf(fp, " backup_src_size: %lx\n", sd->backup_src_size);
|
|
fprintf(fp, " backup_offset: %llx\n", (ulonglong)sd->backup_src_size);
|
|
|
|
for (i = 0; i < sd->sd_list_len; ++i) {
|
|
struct sadump_diskset_data *sdd = sd->sd_list[i];
|
|
|
|
fprintf(fp, "\n sd_list[%d]: \n", i);
|
|
fprintf(fp, " filename: %s\n", sdd->filename);
|
|
fprintf(fp, " dfd: %d\n", sdd->dfd);
|
|
|
|
fprintf(fp, " header: %lx\n", (ulong)sdd->header);
|
|
sph = sdd->header;
|
|
fprintf(fp, " signature1: %x\n", sph->signature1);
|
|
fprintf(fp, " signature2: %x\n", sph->signature2);
|
|
fprintf(fp, " enable: %u\n", sph->enable);
|
|
fprintf(fp, " reboot: %u\n", sph->reboot);
|
|
fprintf(fp, " compress: %u\n", sph->compress);
|
|
fprintf(fp, " recycle: %u\n", sph->recycle);
|
|
fprintf(fp, " label: (unused)\n");
|
|
fprintf(fp, " sadump_id: %s\n", guid_to_str(&sph->sadump_id, guid, sizeof(guid)));
|
|
fprintf(fp, " disk_set_id: %s\n", guid_to_str(&sph->disk_set_id, guid, sizeof(guid)));
|
|
fprintf(fp, " vol_id: %s\n", guid_to_str(&sph->vol_id, guid, sizeof(guid)));
|
|
fprintf(fp, " time_stamp: %s\n",
|
|
strip_linefeeds(asctime(efi_time_t_to_tm(&sph->time_stamp))));
|
|
fprintf(fp, " set_disk_set: %u\n", sph->set_disk_set);
|
|
fprintf(fp, " reserve: %u\n", sph->reserve);
|
|
fprintf(fp, " used_device: %llu\n", (ulonglong)sph->used_device);
|
|
fprintf(fp, " magicnum: %s\n",
|
|
verify_magic_number(sph->magicnum)
|
|
? "(valid)" : "(invalid)");
|
|
|
|
fprintf(fp, " data_offset: %lx\n", sdd->data_offset);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static ulong
|
|
per_cpu_ptr(ulong ptr, int cpu)
|
|
{
|
|
if (cpu < 0 || cpu >= kt->cpus)
|
|
return 0UL;
|
|
|
|
if (kt->cpus == 1)
|
|
return ptr;
|
|
|
|
if (!(kt->flags & PER_CPU_OFF))
|
|
return 0UL;
|
|
|
|
if (machine_type("X86_64")) {
|
|
ulong __per_cpu_load;
|
|
|
|
readmem(symbol_value("__per_cpu_load"), KVADDR,
|
|
&__per_cpu_load, sizeof(__per_cpu_load),
|
|
"__per_cpu_load", FAULT_ON_ERROR);
|
|
|
|
if (kt->__per_cpu_offset[cpu] == __per_cpu_load)
|
|
return 0UL;
|
|
|
|
} else if (machine_type("X86")) {
|
|
if (kt->__per_cpu_offset[cpu] == 0)
|
|
return 0UL;
|
|
|
|
}
|
|
|
|
return ptr + kt->__per_cpu_offset[cpu];
|
|
}
|
|
|
|
static ulong
|
|
early_per_cpu_ptr(char *symbol, struct syment *sym, int cpu)
|
|
{
|
|
char sym_early_ptr[BUFSIZE], sym_early_map[BUFSIZE];
|
|
ulong early_ptr;
|
|
|
|
if (cpu < 0 || cpu >= kt->cpus)
|
|
return 0UL;
|
|
|
|
if (!sym && !(sym = per_cpu_symbol_search(symbol)))
|
|
return 0UL;
|
|
|
|
if (!(kt->flags & SMP))
|
|
return per_cpu_ptr(sym->value, cpu);
|
|
|
|
snprintf(sym_early_ptr, BUFSIZE, "%s_early_ptr", symbol);
|
|
snprintf(sym_early_map, BUFSIZE, "%s_early_map", symbol);
|
|
|
|
if (!symbol_exists(sym_early_ptr) || !symbol_exists(sym_early_map))
|
|
return 0UL;
|
|
|
|
readmem(symbol_value(sym_early_ptr), KVADDR, &early_ptr,
|
|
sizeof(early_ptr), sym_early_ptr, FAULT_ON_ERROR);
|
|
|
|
return early_ptr
|
|
? symbol_value(sym_early_map)+cpu*sizeof(uint16_t)
|
|
: per_cpu_ptr(sym->value, cpu);
|
|
}
|
|
|
|
static ulong
|
|
legacy_per_cpu_ptr(ulong ptr, int cpu)
|
|
{
|
|
ulong addr;
|
|
|
|
if (!(kt->flags & SMP))
|
|
return ptr;
|
|
|
|
if (cpu < 0 || cpu >= kt->cpus)
|
|
return 0UL;
|
|
|
|
if (!readmem(~ptr + cpu * sizeof(ulong), KVADDR, &addr, sizeof(ulong),
|
|
"search percpu_data", FAULT_ON_ERROR))
|
|
return 0UL;
|
|
|
|
return addr;
|
|
}
|
|
|
|
/**
|
|
* Retrieve eip and esp register values from crash_notes saved by
|
|
* kdump at crash. If register values has not been saved yet, set 0 to
|
|
* eip and esp instead.
|
|
*/
|
|
static int
|
|
get_prstatus_from_crash_notes(int cpu, char *prstatus)
|
|
{
|
|
ulong crash_notes, crash_notes_ptr, percpu_addr;
|
|
char *prstatus_ptr, *note_buf, *zero_buf, *name;
|
|
uint32_t *buf;
|
|
|
|
if (cpu < 0 || kt->cpus <= cpu) {
|
|
error(INFO, "sadump: given cpu is invalid: %d\n", cpu);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!symbol_exists("crash_notes")) {
|
|
error(INFO, "sadump: symbol crash_notes doesn't exist\n");
|
|
return FALSE;
|
|
}
|
|
|
|
crash_notes = symbol_value("crash_notes");
|
|
|
|
readmem(crash_notes, KVADDR, &crash_notes_ptr, sizeof(ulong),
|
|
"dereference crash_notes", FAULT_ON_ERROR);
|
|
|
|
if (!crash_notes_ptr) {
|
|
if (CRASHDEBUG(1))
|
|
error(INFO,
|
|
"sadump: buffer for crash_notes is NULL\n");
|
|
return FALSE;
|
|
}
|
|
|
|
percpu_addr = VALID_STRUCT(percpu_data)
|
|
? legacy_per_cpu_ptr(crash_notes_ptr, cpu)
|
|
: per_cpu_ptr(crash_notes_ptr, cpu);
|
|
|
|
zero_buf = GETBUF(SIZE(note_buf));
|
|
BZERO(zero_buf, SIZE(note_buf));
|
|
|
|
note_buf = GETBUF(SIZE(note_buf));
|
|
|
|
readmem(percpu_addr, KVADDR, note_buf, SIZE(note_buf),
|
|
"read crash_notes", FAULT_ON_ERROR);
|
|
|
|
if (memcmp(note_buf, zero_buf, SIZE(note_buf)) == 0)
|
|
return FALSE;
|
|
|
|
if (BITS64()) {
|
|
Elf64_Nhdr *note64;
|
|
|
|
note64 = (Elf64_Nhdr *)note_buf;
|
|
buf = (uint32_t *)note_buf;
|
|
name = (char *)(note64 + 1);
|
|
|
|
if (note64->n_type != NT_PRSTATUS ||
|
|
note64->n_namesz != strlen("CORE") + 1 ||
|
|
strncmp(name, "CORE", note64->n_namesz) ||
|
|
note64->n_descsz != SIZE(elf_prstatus))
|
|
return FALSE;
|
|
|
|
prstatus_ptr = (char *)(buf + (sizeof(*note64) + 3) / 4 +
|
|
(note64->n_namesz + 3) / 4);
|
|
|
|
} else {
|
|
Elf32_Nhdr *note32;
|
|
|
|
note32 = (Elf32_Nhdr *)note_buf;
|
|
buf = (uint32_t *)note_buf;
|
|
name = (char *)(note32 + 1);
|
|
|
|
if ((note32->n_type != NT_PRSTATUS) &&
|
|
(note32->n_namesz != strlen("CORE") + 1 ||
|
|
strncmp(name, "CORE", note32->n_namesz) ||
|
|
note32->n_descsz != SIZE(elf_prstatus)))
|
|
return FALSE;
|
|
|
|
prstatus_ptr = (char *)(buf + (sizeof(*note32) + 3) / 4 +
|
|
(note32->n_namesz + 3) / 4);
|
|
|
|
}
|
|
|
|
memcpy(prstatus, prstatus_ptr, SIZE(elf_prstatus));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int
|
|
sadump_get_smram_cpu_state(int apicid,
|
|
struct sadump_smram_cpu_state *smram)
|
|
{
|
|
ulong offset;
|
|
|
|
if (!sd->sub_hdr_offset || !sd->smram_cpu_state_size ||
|
|
apicid >= sd->dump_header->nr_cpus)
|
|
return FALSE;
|
|
|
|
offset = sd->sub_hdr_offset + sizeof(uint32_t) +
|
|
sd->dump_header->nr_cpus * sizeof(struct sadump_apic_state);
|
|
|
|
if (lseek(sd->dfd, offset + apicid * sd->smram_cpu_state_size,
|
|
SEEK_SET) == failed)
|
|
error(FATAL,
|
|
"sadump: cannot lseek smram cpu state in dump sub header\n");
|
|
|
|
if (read(sd->dfd, smram, sd->smram_cpu_state_size) != sd->smram_cpu_state_size)
|
|
error(FATAL, "sadump: cannot read smram cpu state in dump sub "
|
|
"header\n");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
display_smram_cpu_state(int apicid, struct sadump_smram_cpu_state *s)
|
|
{
|
|
fprintf(fp,
|
|
"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,
|
|
(ulonglong)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,
|
|
(ulonglong)s->Cr0, (ulonglong)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, (ulonglong)s->Rflags,
|
|
(ulonglong)s->Eptp, s->EptpSetting,
|
|
(ulonglong)s->Dr6, (ulonglong)s->Dr7,
|
|
(ulonglong)s->Ia32Efer,
|
|
s->IoMemAddrUpper, s->IoMemAddrLower, (ulonglong)s->IoEip,
|
|
s->IoMisc, s->LdtInfo,
|
|
s->IoInstructionRestart,
|
|
s->AutoHaltRestart);
|
|
}
|
|
|
|
static int cpu_to_apicid(int cpu, int *apicid)
|
|
{
|
|
struct syment *sym;
|
|
|
|
if (symbol_exists("bios_cpu_apicid")) {
|
|
uint8_t apicid_u8;
|
|
|
|
readmem(symbol_value("bios_cpu_apicid") + cpu*sizeof(uint8_t),
|
|
KVADDR, &apicid_u8, sizeof(uint8_t), "bios_cpu_apicid",
|
|
FAULT_ON_ERROR);
|
|
|
|
*apicid = (int)apicid_u8;
|
|
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "sadump: apicid %u for cpu %d from "
|
|
"bios_cpu_apicid\n", apicid_u8, cpu);
|
|
|
|
} else if ((sym = per_cpu_symbol_search("x86_bios_cpu_apicid"))) {
|
|
uint16_t apicid_u16;
|
|
|
|
readmem(early_per_cpu_ptr("x86_bios_cpu_apicid", sym, cpu),
|
|
KVADDR, &apicid_u16, sizeof(uint16_t),
|
|
"x86_bios_cpu_apicid", FAULT_ON_ERROR);
|
|
|
|
*apicid = (int)apicid_u16;
|
|
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "sadump: apicid %u for cpu %d from "
|
|
"x86_bios_cpu_apicid\n", apicid_u16, cpu);
|
|
|
|
} else {
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "sadump: no symbols for access to apicid\n");
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static int
|
|
get_sadump_smram_cpu_state(int cpu, struct sadump_smram_cpu_state *smram)
|
|
{
|
|
int apicid = 0;
|
|
|
|
if (cpu < 0 || kt->cpus <= cpu) {
|
|
error(INFO, "sadump: given cpu is invalid: %d\n", cpu);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!cpu_to_apicid(cpu, &apicid))
|
|
return FALSE;
|
|
|
|
sadump_get_smram_cpu_state(apicid, smram);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void get_sadump_regs(struct bt_info *bt, ulong *ipp, ulong *spp)
|
|
{
|
|
ulong ip, sp;
|
|
struct sadump_smram_cpu_state smram;
|
|
char *prstatus;
|
|
int cpu = bt->tc->processor;
|
|
|
|
if (!is_task_active(bt->task)) {
|
|
machdep->get_stack_frame(bt, ipp, spp);
|
|
return;
|
|
}
|
|
|
|
bt->flags |= BT_DUMPFILE_SEARCH;
|
|
if (machine_type("X86_64"))
|
|
machdep->get_stack_frame(bt, ipp, spp);
|
|
else if (machine_type("X86"))
|
|
get_netdump_regs_x86(bt, ipp, spp);
|
|
if (bt->flags & BT_DUMPFILE_SEARCH)
|
|
return;
|
|
|
|
prstatus = GETBUF(SIZE(elf_prstatus));
|
|
|
|
if (get_prstatus_from_crash_notes(cpu, prstatus)) {
|
|
ip = ULONG(prstatus +
|
|
OFFSET(elf_prstatus_pr_reg) +
|
|
(BITS64()
|
|
? OFFSET(user_regs_struct_rip)
|
|
: OFFSET(user_regs_struct_eip)));
|
|
sp = ULONG(prstatus +
|
|
OFFSET(elf_prstatus_pr_reg) +
|
|
(BITS64()
|
|
? OFFSET(user_regs_struct_rsp)
|
|
: OFFSET(user_regs_struct_eip)));
|
|
if (ip || sp) {
|
|
*ipp = ip;
|
|
*spp = sp;
|
|
return;
|
|
}
|
|
}
|
|
|
|
get_sadump_smram_cpu_state(cpu, &smram);
|
|
ip = smram.Rip;
|
|
sp = ((uint64_t)smram.RspUpper << 32) + smram.RspLower;
|
|
|
|
if (is_kernel_text(ip) &&
|
|
(((sp >= GET_STACKBASE(bt->task)) &&
|
|
(sp < GET_STACKTOP(bt->task))) ||
|
|
in_alternate_stack(bt->tc->processor, sp))) {
|
|
*ipp = ip;
|
|
*spp = sp;
|
|
bt->flags |= BT_KERNEL_SPACE;
|
|
return;
|
|
}
|
|
|
|
if (!is_kernel_text(ip) &&
|
|
in_user_stack(bt->tc->task, sp))
|
|
bt->flags |= BT_USER_SPACE;
|
|
|
|
}
|
|
|
|
void
|
|
sadump_display_regs(int cpu, FILE *ofp)
|
|
{
|
|
struct sadump_smram_cpu_state smram;
|
|
|
|
if (cpu < 0 || cpu >= kt->cpus) {
|
|
error(INFO, "sadump: given cpu is invalid: %d\n", cpu);
|
|
return;
|
|
}
|
|
|
|
get_sadump_smram_cpu_state(cpu, &smram);
|
|
|
|
if (machine_type("X86_64")) {
|
|
fprintf(ofp,
|
|
" RIP: %016llx RSP: %016llx RFLAGS: %08llx\n"
|
|
" RAX: %016llx RBX: %016llx RCX: %016llx\n"
|
|
" RDX: %016llx RSI: %016llx RDI: %016llx\n"
|
|
" RBP: %016llx R8: %016llx R9: %016llx\n"
|
|
" R10: %016llx R11: %016llx R12: %016llx\n"
|
|
" R13: %016llx R14: %016llx R15: %016llx\n"
|
|
" CS: %04x SS: %04x\n",
|
|
(ulonglong)(smram.Rip),
|
|
(ulonglong)(((uint64_t)smram.RspUpper<<32)+smram.RspLower),
|
|
(ulonglong)(smram.Rflags),
|
|
(ulonglong)(((uint64_t)smram.RaxUpper<<32)+smram.RaxLower),
|
|
(ulonglong)(((uint64_t)smram.RbxUpper<<32)+smram.RbxLower),
|
|
(ulonglong)(((uint64_t)smram.RcxUpper<<32)+smram.RcxLower),
|
|
(ulonglong)(((uint64_t)smram.RdxUpper<<32)+smram.RdxLower),
|
|
(ulonglong)(((uint64_t)smram.RsiUpper<<32)+smram.RsiLower),
|
|
(ulonglong)(((uint64_t)smram.RdiUpper<<32)+smram.RdiLower),
|
|
(ulonglong)(((uint64_t)smram.RbpUpper<<32)+smram.RbpLower),
|
|
(ulonglong)(((uint64_t)smram.R8Upper<<32)+smram.R8Lower),
|
|
(ulonglong)(((uint64_t)smram.R9Upper<<32)+smram.R9Lower),
|
|
(ulonglong)(((uint64_t)smram.R10Upper<<32)+smram.R10Lower),
|
|
(ulonglong)(((uint64_t)smram.R11Upper<<32)+smram.R11Lower),
|
|
(ulonglong)(((uint64_t)smram.R12Upper<<32)+smram.R12Lower),
|
|
(ulonglong)(((uint64_t)smram.R13Upper<<32)+smram.R13Lower),
|
|
(ulonglong)(((uint64_t)smram.R14Upper<<32)+smram.R14Lower),
|
|
(ulonglong)(((uint64_t)smram.R15Upper<<32)+smram.R15Lower),
|
|
smram.Cs,
|
|
smram.Ss);
|
|
}
|
|
|
|
if (machine_type("X86")) {
|
|
fprintf(ofp,
|
|
" EAX: %08llx EBX: %08llx ECX: %08llx EDX: %08llx\n"
|
|
" DS: %04x ESI: %08llx ES: %04x EDI: %08llx\n"
|
|
" SS: %04x ESP: %08llx EBP: %08llx GS: %04x\n"
|
|
" CS: %04x EIP: %08llx EFLAGS: %08llx\n",
|
|
(ulonglong)smram.RaxLower,
|
|
(ulonglong)smram.RbxLower,
|
|
(ulonglong)smram.RcxLower,
|
|
(ulonglong)smram.RdxLower,
|
|
smram.Ds & 0xffff,
|
|
(ulonglong)smram.RsiLower,
|
|
smram.Es & 0xffff,
|
|
(ulonglong)smram.RdiLower,
|
|
smram.Ss,
|
|
(ulonglong)smram.RspLower,
|
|
(ulonglong)smram.RbpLower,
|
|
smram.Gs,
|
|
smram.Cs,
|
|
(ulonglong)smram.Rip,
|
|
(ulonglong)smram.Rflags);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* sadump does not save phys_base; it must resort to another way.
|
|
*/
|
|
int sadump_phys_base(ulong *phys_base)
|
|
{
|
|
if (SADUMP_VALID() && !sd->phys_base) {
|
|
if (CRASHDEBUG(1))
|
|
error(NOTE, "sadump: does not save phys_base.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if (sd->phys_base) {
|
|
*phys_base = sd->phys_base;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
sadump_set_phys_base(ulong phys_base)
|
|
{
|
|
sd->phys_base = phys_base;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Used by "sys" command to show diskset disk names.
|
|
*/
|
|
void sadump_show_diskset(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < sd->sd_list_len; ++i) {
|
|
char *filename = sd->sd_list[i]->filename;
|
|
|
|
fprintf(fp, "%s%s", i ? " " : "",
|
|
filename);
|
|
if ((i+1) < sd->sd_list_len)
|
|
fprintf(fp, "\n");
|
|
}
|
|
}
|
|
|
|
static int block_table_init(void)
|
|
{
|
|
uint64_t pfn, section, max_section, *block_table;
|
|
|
|
max_section = divideup(sd->max_mapnr, SADUMP_PF_SECTION_NUM);
|
|
|
|
block_table = calloc(sizeof(uint64_t), max_section);
|
|
if (!block_table) {
|
|
error(INFO, "sadump: cannot allocate memory for block_table\n");
|
|
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 (page_is_dumpable(pfn))
|
|
block_table[section]++;
|
|
}
|
|
|
|
sd->block_table = block_table;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static uint64_t pfn_to_block(uint64_t pfn)
|
|
{
|
|
uint64_t block, section, p;
|
|
|
|
section = pfn / SADUMP_PF_SECTION_NUM;
|
|
|
|
if (section)
|
|
block = sd->block_table[section - 1];
|
|
else
|
|
block = 0;
|
|
|
|
for (p = section * SADUMP_PF_SECTION_NUM; p < pfn; ++p)
|
|
if (page_is_dumpable(p))
|
|
block++;
|
|
|
|
return block;
|
|
}
|
|
|
|
int sadump_is_zero_excluded(void)
|
|
{
|
|
return (sd->flags & SADUMP_ZERO_EXCLUDED) ? TRUE : FALSE;
|
|
}
|
|
|
|
void sadump_set_zero_excluded(void)
|
|
{
|
|
sd->flags |= SADUMP_ZERO_EXCLUDED;
|
|
}
|
|
|
|
void sadump_unset_zero_excluded(void)
|
|
{
|
|
sd->flags &= ~SADUMP_ZERO_EXCLUDED;
|
|
}
|
|
|
|
struct sadump_data *
|
|
get_sadump_data(void)
|
|
{
|
|
return sd;
|
|
}
|
|
|
|
#ifdef X86_64
|
|
static int
|
|
get_sadump_smram_cpu_state_any(struct sadump_smram_cpu_state *smram)
|
|
{
|
|
ulong offset;
|
|
struct sadump_header *sh = sd->dump_header;
|
|
int apicid;
|
|
struct sadump_smram_cpu_state scs, zero;
|
|
|
|
offset = sd->sub_hdr_offset + sizeof(uint32_t) +
|
|
sd->dump_header->nr_cpus * sizeof(struct sadump_apic_state);
|
|
|
|
memset(&zero, 0, sizeof(zero));
|
|
|
|
for (apicid = 0; apicid < sh->nr_cpus; ++apicid) {
|
|
if (!read_device(&scs, sizeof(scs), &offset)) {
|
|
error(INFO, "sadump: cannot read sub header "
|
|
"cpu_state\n");
|
|
return FALSE;
|
|
}
|
|
if (memcmp(&scs, &zero, sizeof(scs)) != 0) {
|
|
*smram = scs;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
sadump_get_cr3_idtr(ulong *cr3, ulong *idtr)
|
|
{
|
|
struct sadump_smram_cpu_state scs;
|
|
|
|
memset(&scs, 0, sizeof(scs));
|
|
get_sadump_smram_cpu_state_any(&scs);
|
|
|
|
*cr3 = scs.Cr3;
|
|
*idtr = ((uint64_t)scs.IdtUpper)<<32 | (uint64_t)scs.IdtLower;
|
|
|
|
return TRUE;
|
|
}
|
|
#endif /* X86_64 */
|