1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
|
/*
* zgetdump - Tool for copying and converting System z dumps
*
* /dev/mem and /dev/crash dump input format
*
* Copyright IBM Corp. 2012, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <fcntl.h>
#include <limits.h>
#include <linux/fs.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <time.h>
#include <unistd.h>
#include "zgetdump.h"
#include "df_s390.h"
#include "dfi.h"
#include "dfi_mem_chunk.h"
/*
* Add live dump magic to buffer
*/
static void add_live_magic(void *buf, u64 off, u64 cnt)
{
if (off >= sizeof(dfi_live_dump_magic))
return;
memcpy(buf, &dfi_live_dump_magic,
MIN(cnt, sizeof(dfi_live_dump_magic)));
}
/*
* "devmem" mem chunk read callback
*
* This functions reads page wise in order to detect unreadable pages
*/
static void dfi_devmem_mem_chunk_read(struct dfi_mem_chunk *mem_chunk, u64 off,
void *buf, u64 cnt)
{
u64 copied = 0, len = PAGE_SIZE;
(void) mem_chunk;
if (off % PAGE_SIZE)
len = MIN(len, PAGE_SIZE - off % PAGE_SIZE);
len = MIN(len, cnt);
do {
zg_seek(g.fh, mem_chunk->start + off + copied, ZG_CHECK);
if (zg_read(g.fh, buf + copied, len, ZG_CHECK_NONE) < 0) {
if (errno == EFAULT) {
/* This can happen when using CMM */
memset(buf + copied, 0, len);
} else {
ERR_EXIT_ERRNO("Could not read %s",
g.opts.device);
}
}
copied += len;
len = MIN(PAGE_SIZE, cnt - copied);
} while (len);
add_live_magic(buf, mem_chunk->start + off, cnt);
}
/*
* Detect memory chunks via /proc/iomem
*
* check = 0: Initialize memory map
* check = 1: Verifiy that current system memory map is same as DFI memory map
*/
static int detect_mem_chunks(int check)
{
char line[4096], type1[4096], type2[4096];
unsigned long start, end, cnt = 0;
struct dfi_mem_chunk *mem_chunk;
struct zg_fh *fh;
ssize_t rc;
fh = zg_open("/proc/iomem", O_RDONLY, ZG_CHECK);
do {
rc = zg_gets(fh, line, sizeof(line), ZG_CHECK);
if (rc == 0)
break;
sscanf(line, "%lx-%lx : %s %s", &start, &end, type1, type2);
if (strcmp(type1, "System") != 0)
continue;
if (strcmp(type2, "RAM") != 0 && strcmp(type2, "ROM") != 0)
continue;
if (check) {
mem_chunk = dfi_mem_chunk_find(start);
if (!mem_chunk)
return -EINVAL;
if (mem_chunk->start != start)
return -EINVAL;
if (mem_chunk->end != end)
return -EINVAL;
cnt++;
} else {
dfi_mem_chunk_add(start, end - start + 1, NULL,
dfi_devmem_mem_chunk_read, NULL);
}
} while (1);
if (check && cnt != dfi_mem_chunk_cnt())
return -EINVAL;
return 0;
}
/*
* Verify architecture of running system
*/
static void check_system_arch(void)
{
struct utsname utsname;
uname(&utsname);
if (memcmp(utsname.machine, "s390x", 5) != 0)
ERR_EXIT("Dump architecture \"%s\" is not supported",
utsname.machine);
}
/*
* Initialize devmem DFI
*/
static int dfi_devmem_init(void)
{
if (strcmp(g.fh->path, "/dev/mem") != 0 &&
strcmp(g.fh->path, "/dev/crash") != 0)
return -ENODEV;
check_system_arch();
dfi_cpu_info_init(DFI_CPU_CONTENT_NONE);
detect_mem_chunks(0);
dfi_attr_dump_method_set(DFI_DUMP_METHOD_LIVE);
zg_seek(g.fh, 0, ZG_CHECK);
return 0;
}
/*
* Cleanup devmem DFI
*/
static void dfi_devmem_exit(void)
{
if (detect_mem_chunks(1))
STDERR("Warning: memory map has changed\n");
}
/*
* devmem DFI operations
*/
struct dfi dfi_devmem = {
.name = "devmem",
.init = dfi_devmem_init,
.exit = dfi_devmem_exit,
.feat_bits = DFI_FEAT_COPY | DFI_FEAT_SEEK,
};
|