mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Since memory chunks can overlap for vr-kernel dumps stored in elf format (ngdump, zfcpdump), we need to consider this when converting to s390 dump output format. For that sort DFI memory chunks by start address and adjust dfo_s390 logic for identifying memory gaps. Otherwise we might end up with bogus DFO memory chunks being created. Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com> Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
552 lines
12 KiB
C
552 lines
12 KiB
C
/*
|
|
* Copyright IBM Corp. 2001, 2018
|
|
*
|
|
* 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 <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include "lib/util_libc.h"
|
|
#include "lib/util_log.h"
|
|
|
|
#include "zg.h"
|
|
#include "dfi_mem_chunk.h"
|
|
|
|
/*
|
|
* Memory information
|
|
*/
|
|
struct mem {
|
|
struct dfi_mem_chunk *chunk_cache;
|
|
u64 start_addr;
|
|
u64 end_addr;
|
|
unsigned int chunk_cnt;
|
|
struct util_list chunk_list;
|
|
};
|
|
|
|
/*
|
|
* File local static data
|
|
*/
|
|
static struct {
|
|
struct mem mem_phys;
|
|
struct mem mem_virt;
|
|
} l;
|
|
|
|
/*
|
|
* Initialize DFI memory chunks
|
|
*/
|
|
static void mem_init(struct mem *mem)
|
|
{
|
|
mem->start_addr = U64_MAX;
|
|
mem->end_addr = 0;
|
|
util_list_init(&mem->chunk_list, struct dfi_mem_chunk, list);
|
|
}
|
|
|
|
/*
|
|
* Memory chunk compare function for list sorting
|
|
*/
|
|
static int mem_chunk_cmp_fn(void *a, void *b, void *UNUSED(data))
|
|
{
|
|
struct dfi_mem_chunk *mem_chunk1 = a;
|
|
struct dfi_mem_chunk *mem_chunk2 = b;
|
|
|
|
return mem_chunk1->start < mem_chunk2->start ? -1 : 1;
|
|
}
|
|
|
|
/*
|
|
* Update DFI memory chunks
|
|
*/
|
|
static void mem_update(struct mem *mem)
|
|
{
|
|
struct dfi_mem_chunk *mem_chunk;
|
|
|
|
util_list_sort(&mem->chunk_list, mem_chunk_cmp_fn, NULL);
|
|
mem->start_addr = U64_MAX;
|
|
mem->end_addr = 0;
|
|
util_list_iterate(&mem->chunk_list, mem_chunk) {
|
|
mem->start_addr = MIN(mem->start_addr, mem_chunk->start);
|
|
mem->end_addr = MAX(mem->end_addr, mem_chunk->end);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Print memory map
|
|
*/
|
|
void dfi_mem_map_print(bool verbose)
|
|
{
|
|
struct dfi_mem_chunk *mem_chunk;
|
|
u64 print_start = 0, print_end = 0;
|
|
const char *zero_str;
|
|
u32 volnr = 0;
|
|
|
|
STDERR("\nMemory map:\n");
|
|
/*
|
|
* Print each memory chunk if verbose specified
|
|
*/
|
|
if (verbose) {
|
|
dfi_mem_chunk_iterate(mem_chunk) {
|
|
zero_str = "";
|
|
if (mem_chunk->read_fn == dfi_mem_chunk_read_zero)
|
|
zero_str = " zeroes";
|
|
STDERR(" %016llx - %016llx (%llu MB%s)\n",
|
|
mem_chunk->start, mem_chunk->end,
|
|
TO_MIB(mem_chunk->size), zero_str);
|
|
}
|
|
return;
|
|
}
|
|
/*
|
|
* Merge adjacent memory chunks from the same volume
|
|
*/
|
|
dfi_mem_chunk_iterate(mem_chunk) {
|
|
if (print_end == 0) {
|
|
print_start = mem_chunk->start;
|
|
print_end = mem_chunk->end;
|
|
volnr = mem_chunk->volnr;
|
|
continue;
|
|
}
|
|
if (mem_chunk->start != print_end + 1 ||
|
|
mem_chunk->volnr != volnr) {
|
|
STDERR(" %016llx - %016llx (%llu MB)\n", print_start,
|
|
print_end, TO_MIB(print_end - print_start + 1));
|
|
print_start = mem_chunk->start;
|
|
volnr = mem_chunk->volnr;
|
|
}
|
|
print_end = mem_chunk->end;
|
|
}
|
|
STDERR(" %016llx - %016llx (%llu MB)\n", print_start,
|
|
print_end, TO_MIB(print_end - print_start + 1));
|
|
}
|
|
|
|
/*
|
|
* Check if memory chunk contains address
|
|
*/
|
|
static int mem_chunk_has_addr(struct dfi_mem_chunk *mem_chunk, u64 addr)
|
|
{
|
|
return (addr >= mem_chunk->start && addr <= mem_chunk->end);
|
|
}
|
|
|
|
/*
|
|
* Find memory chunk that contains address
|
|
*/
|
|
static struct dfi_mem_chunk *mem_chunk_find(struct mem *mem, u64 addr)
|
|
{
|
|
struct dfi_mem_chunk *mem_chunk;
|
|
|
|
if (mem->chunk_cache && mem_chunk_has_addr(mem->chunk_cache, addr))
|
|
return mem->chunk_cache;
|
|
util_list_iterate(&mem->chunk_list, mem_chunk) {
|
|
if (mem_chunk_has_addr(mem_chunk, addr)) {
|
|
mem->chunk_cache = mem_chunk;
|
|
return mem_chunk;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Is memory range valid?
|
|
*/
|
|
static int mem_range_valid(struct mem *mem, u64 addr, u64 len)
|
|
{
|
|
struct dfi_mem_chunk *mem_chunk;
|
|
u64 addr_end = addr + len;
|
|
|
|
/* check for unsigned wrap */
|
|
if (addr_end < addr)
|
|
return 0;
|
|
|
|
do {
|
|
mem_chunk = mem_chunk_find(mem, addr);
|
|
if (!mem_chunk)
|
|
return 0;
|
|
addr += MIN(len, mem_chunk->end - addr + 1);
|
|
} while (addr < addr_end);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Is memory already mapped at range?
|
|
*/
|
|
static int mem_range_mapped(u64 start, u64 size)
|
|
{
|
|
struct dfi_mem_chunk *mem_chunk;
|
|
u64 end = start + size - 1;
|
|
|
|
dfi_mem_chunk_iterate(mem_chunk) {
|
|
if (mem_chunk->start > end)
|
|
continue;
|
|
if (mem_chunk->end < start)
|
|
continue;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Add memory chunk to memory
|
|
*/
|
|
static void mem_chunk_create(struct mem *mem, u64 start, u64 size, void *data,
|
|
dfi_mem_chunk_read_fn read_fn,
|
|
dfi_mem_chunk_free_fn free_fn)
|
|
{
|
|
struct dfi_mem_chunk *mem_chunk;
|
|
|
|
mem_chunk = util_malloc(sizeof(*mem_chunk));
|
|
mem_chunk->start = start;
|
|
mem_chunk->end = start + size - 1;
|
|
mem_chunk->size = size;
|
|
mem_chunk->read_fn = read_fn;
|
|
mem_chunk->free_fn = free_fn;
|
|
mem_chunk->data = data;
|
|
|
|
util_list_add_tail(&mem->chunk_list, mem_chunk);
|
|
mem->start_addr = MIN(mem->start_addr, mem_chunk->start);
|
|
mem->end_addr = MAX(mem->end_addr, mem_chunk->end);
|
|
mem->chunk_cache = mem_chunk;
|
|
mem->chunk_cnt++;
|
|
}
|
|
|
|
/*
|
|
* Read memory at given address
|
|
*/
|
|
static void mem_read(struct mem *mem, u64 addr, void *buf, size_t cnt)
|
|
{
|
|
struct dfi_mem_chunk *mem_chunk;
|
|
u64 size, off, copied = 0;
|
|
|
|
while (copied != cnt) {
|
|
mem_chunk = mem_chunk_find(mem, addr);
|
|
size = MIN(cnt - copied, mem_chunk->end - addr + 1);
|
|
off = addr - mem_chunk->start;
|
|
mem_chunk->read_fn(mem_chunk, off, buf + copied, size);
|
|
copied += size;
|
|
addr += size;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Read memory for virtual map memory chunk
|
|
*/
|
|
static void mem_chunk_map_read_fn(struct dfi_mem_chunk *mem_chunk, u64 off,
|
|
void *buf, u64 cnt)
|
|
{
|
|
u64 *start = mem_chunk->data;
|
|
|
|
dfi_mem_phys_read(*start + off, buf, cnt);
|
|
}
|
|
|
|
/*
|
|
* Check if memory chunk is a virtual mapping
|
|
*/
|
|
static int mem_chunk_is_map(struct dfi_mem_chunk *mem_chunk)
|
|
{
|
|
return mem_chunk->read_fn == mem_chunk_map_read_fn;
|
|
}
|
|
|
|
/*
|
|
* Return physical start address for memory chunk
|
|
*/
|
|
static u64 mem_chunk_start_phys(struct dfi_mem_chunk *mem_chunk)
|
|
{
|
|
if (mem_chunk_is_map(mem_chunk))
|
|
return *((u64 *) mem_chunk->data);
|
|
else
|
|
return mem_chunk->start;
|
|
}
|
|
|
|
/*
|
|
* Add virtual memory chunk
|
|
*/
|
|
static void mem_chunk_virt_add(u64 start, u64 size, void *data,
|
|
dfi_mem_chunk_read_fn read_fn,
|
|
dfi_mem_chunk_free_fn free_fn)
|
|
{
|
|
util_log_print(UTIL_LOG_DEBUG,
|
|
"DFI add %svirt mem chunk start 0x%016lx size 0x%016lx\n",
|
|
read_fn == dfi_mem_chunk_read_zero ? "zero " : "",
|
|
start, size);
|
|
|
|
if (size == 0)
|
|
return;
|
|
mem_chunk_create(&l.mem_virt, start, size, data, read_fn, free_fn);
|
|
}
|
|
|
|
/*
|
|
* Add virtual memory chunk with simple virtual mapping
|
|
*/
|
|
static void mem_chunk_map_add(u64 start, u64 size, u64 start_p)
|
|
{
|
|
u64 *data = util_malloc(sizeof(*data));
|
|
|
|
*data = start_p;
|
|
mem_chunk_virt_add(start, size, data, mem_chunk_map_read_fn, free);
|
|
}
|
|
|
|
/*
|
|
* Add memory chunk with volume index
|
|
*/
|
|
void dfi_mem_chunk_add_vol(u64 start, u64 size, void *data,
|
|
dfi_mem_chunk_read_fn read_fn,
|
|
dfi_mem_chunk_free_fn free_fn,
|
|
u32 volnr)
|
|
{
|
|
util_log_print(UTIL_LOG_DEBUG,
|
|
"DFI add %svol mem chunk start 0x%016lx size 0x%016lx volnr %u\n",
|
|
read_fn == dfi_mem_chunk_read_zero ? "zero " : "",
|
|
start, size, volnr);
|
|
|
|
if (size == 0)
|
|
return;
|
|
mem_chunk_create(&l.mem_phys, start, size, data, read_fn, free_fn);
|
|
mem_chunk_create(&l.mem_virt, start, size, data, read_fn, NULL);
|
|
l.mem_virt.chunk_cache->volnr = volnr;
|
|
|
|
}
|
|
|
|
/*
|
|
* Add memory chunk
|
|
*/
|
|
void dfi_mem_chunk_add(u64 start, u64 size, void *data,
|
|
dfi_mem_chunk_read_fn read_fn,
|
|
dfi_mem_chunk_free_fn free_fn)
|
|
{
|
|
dfi_mem_chunk_add_vol(start, size, data, read_fn, free_fn, 0);
|
|
}
|
|
|
|
/*
|
|
* Read zero pages
|
|
*/
|
|
void dfi_mem_chunk_read_zero(struct dfi_mem_chunk *UNUSED(mem_chunk),
|
|
u64 UNUSED(off), void *buf, u64 cnt)
|
|
{
|
|
memset(buf, 0, cnt);
|
|
}
|
|
|
|
/*
|
|
* Sort memory chunks by start value
|
|
*/
|
|
void dfi_mem_chunk_sort(void)
|
|
{
|
|
util_list_sort(&l.mem_virt.chunk_list, mem_chunk_cmp_fn, NULL);
|
|
}
|
|
|
|
/*
|
|
* Return mem_chunk list head
|
|
*/
|
|
struct util_list *dfi_mem_chunk_list(void)
|
|
{
|
|
return &l.mem_virt.chunk_list;
|
|
}
|
|
|
|
/*
|
|
* Return number of memory chunks in input dump
|
|
*/
|
|
unsigned int dfi_mem_chunk_cnt(void)
|
|
{
|
|
return l.mem_virt.chunk_cnt;
|
|
}
|
|
|
|
/*
|
|
* Return maximum memory range
|
|
*/
|
|
u64 dfi_mem_range(void)
|
|
{
|
|
if (l.mem_virt.start_addr == U64_MAX)
|
|
return 0;
|
|
return l.mem_virt.end_addr - l.mem_virt.start_addr + 1;
|
|
}
|
|
|
|
/*
|
|
* Return maximum memory address
|
|
*/
|
|
u64 dfi_mem_end(void)
|
|
{
|
|
return l.mem_virt.end_addr;
|
|
}
|
|
|
|
/*
|
|
* Is memory range valid?
|
|
*/
|
|
int dfi_mem_range_valid(u64 addr, u64 len)
|
|
{
|
|
return mem_range_valid(&l.mem_virt, addr, len);
|
|
}
|
|
|
|
/*
|
|
* Return first memory chunk
|
|
*/
|
|
struct dfi_mem_chunk *dfi_mem_chunk_first(void)
|
|
{
|
|
if (util_list_is_empty(&l.mem_virt.chunk_list))
|
|
return NULL;
|
|
return util_list_start(&l.mem_virt.chunk_list);
|
|
}
|
|
|
|
/*
|
|
* Return last memory chunk
|
|
*/
|
|
struct dfi_mem_chunk *dfi_mem_chunk_last(void)
|
|
{
|
|
if (util_list_is_empty(&l.mem_virt.chunk_list))
|
|
return NULL;
|
|
return util_list_end(&l.mem_virt.chunk_list);
|
|
}
|
|
|
|
/*
|
|
* Return next memory chunk
|
|
*/
|
|
struct dfi_mem_chunk *dfi_mem_chunk_next(struct dfi_mem_chunk *mem_chunk)
|
|
{
|
|
return util_list_next(&l.mem_virt.chunk_list, mem_chunk);
|
|
}
|
|
|
|
/*
|
|
* Return previous memory chunk
|
|
*/
|
|
struct dfi_mem_chunk *dfi_mem_chunk_prev(struct dfi_mem_chunk *mem_chunk)
|
|
{
|
|
return util_list_prev(&l.mem_virt.chunk_list, mem_chunk);
|
|
}
|
|
|
|
/*
|
|
* Find memory chunk for given address
|
|
*/
|
|
struct dfi_mem_chunk *dfi_mem_chunk_find(u64 addr)
|
|
{
|
|
return mem_chunk_find(&l.mem_virt, addr);
|
|
}
|
|
|
|
/*
|
|
* Read physical memory at given address
|
|
*/
|
|
int dfi_mem_phys_read(u64 addr, void *buf, size_t cnt)
|
|
{
|
|
util_log_print(UTIL_LOG_TRACE,
|
|
"DFI phys mem read addr 0x%016lx size 0x%016lx\n",
|
|
addr, cnt);
|
|
if (!mem_range_valid(&l.mem_phys, addr, cnt))
|
|
return -EINVAL;
|
|
mem_read(&l.mem_phys, addr, buf, cnt);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Read virtual memory at given address
|
|
*/
|
|
int dfi_mem_virt_read(u64 addr, void *buf, size_t cnt)
|
|
{
|
|
util_log_print(UTIL_LOG_TRACE,
|
|
"DFI virt mem read addr 0x%016lx size 0x%016lx\n",
|
|
addr, cnt);
|
|
if (!mem_range_valid(&l.mem_virt, addr, cnt))
|
|
return -EINVAL;
|
|
mem_read(&l.mem_virt, addr, buf, cnt);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Unmap memory region
|
|
*/
|
|
void dfi_mem_unmap(u64 start, u64 size)
|
|
{
|
|
u64 start_phys, end_phys, addr_phys, addr_virt, size_virt;
|
|
struct dfi_mem_chunk *mem_chunk, *tmp;
|
|
u64 end = start + size - 1;
|
|
|
|
util_log_print(UTIL_LOG_TRACE,
|
|
"DFI mem unmap start 0x%016lx, size 0x%016lx\n",
|
|
start, size);
|
|
|
|
util_list_iterate_safe(&l.mem_virt.chunk_list, mem_chunk, tmp) {
|
|
/*
|
|
* Chunk not hit?
|
|
*/
|
|
if (mem_chunk->start >= start + size)
|
|
continue;
|
|
if (mem_chunk->end < start)
|
|
continue;
|
|
/*
|
|
* Chunk completely unmapped
|
|
*
|
|
* UNMAP: UUUUUUUUU || UUUUUU
|
|
* CHUNK: CCCC || CCCCCC
|
|
* TO:
|
|
*/
|
|
if (mem_chunk->start >= start && mem_chunk->end <= end)
|
|
goto free;
|
|
|
|
/*
|
|
* Get real start and end addresses
|
|
*/
|
|
start_phys = mem_chunk_start_phys(mem_chunk);
|
|
end_phys = start_phys + mem_chunk->size - 1;
|
|
|
|
/*
|
|
* Chunk hit at start or in the middle?
|
|
*
|
|
* UNMAP: UUUUUU || UU || UUU
|
|
* CHUNK: CCCCC || CCCCCC || CCCC
|
|
* TO: NN || NN || NNN
|
|
*/
|
|
if (mem_chunk->end > end) {
|
|
addr_virt = end + 1;
|
|
size_virt = mem_chunk->end - end;
|
|
addr_phys = end_phys - size_virt + 1;
|
|
mem_chunk_map_add(addr_virt, size_virt, addr_phys);
|
|
}
|
|
/*
|
|
* Chunk hit at end or in the middle?
|
|
*
|
|
* UNMAP: UUUUUU || UU || UUU
|
|
* CHUNK: CCCCC || CCCCCC || CCC
|
|
* TO: NN || NN || NN
|
|
*/
|
|
if (mem_chunk->start < start) {
|
|
addr_virt = mem_chunk->start;
|
|
size_virt = start - addr_virt;
|
|
addr_phys = start_phys;
|
|
mem_chunk_map_add(addr_virt, size_virt, addr_phys);
|
|
}
|
|
free:
|
|
util_list_remove(&l.mem_virt.chunk_list, mem_chunk);
|
|
l.mem_virt.chunk_cnt--;
|
|
if (mem_chunk->data && mem_chunk->free_fn)
|
|
mem_chunk->free_fn(mem_chunk->data);
|
|
free(mem_chunk);
|
|
}
|
|
mem_update(&l.mem_virt);
|
|
}
|
|
|
|
/*
|
|
* Map memory region
|
|
*/
|
|
void dfi_mem_map(u64 start, u64 size, u64 start_phys)
|
|
{
|
|
util_log_print(UTIL_LOG_TRACE,
|
|
"DFI mem map start 0x%016lx, size 0x%016lx, base 0x%016lx\n",
|
|
start, size, start_phys);
|
|
|
|
if (mem_range_mapped(start, size)) {
|
|
dfi_mem_map_print(false);
|
|
ABORT("Map request for already mapped region (%llx/%llx/%llx)",
|
|
start, size, start_phys);
|
|
}
|
|
mem_chunk_map_add(start, size, start_phys);
|
|
mem_update(&l.mem_virt);
|
|
}
|
|
|
|
int dfi_mem_chunk_init(void)
|
|
{
|
|
mem_init(&l.mem_virt);
|
|
mem_init(&l.mem_phys);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void dfi_mem_chunk_deinit(void)
|
|
{
|
|
memset(&l, 0, sizeof(l));
|
|
}
|