mirror of https://github.com/crash-utility/crash
278 lines
6.6 KiB
C
278 lines
6.6 KiB
C
/* xen_dom0.c
|
|
*
|
|
* Copyright (C) 2015 David Anderson
|
|
* Copyright (C) 2015 Red Hat, Inc. All rights reserved.
|
|
*
|
|
* 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: David Anderson
|
|
*/
|
|
|
|
#include "defs.h"
|
|
#include "xen_dom0.h"
|
|
|
|
static struct xen_kdump_data xen_kdump_data = { 0 };
|
|
|
|
struct xen_kdump_data *xkd = &xen_kdump_data;
|
|
|
|
void
|
|
dump_xen_kdump_data(FILE *fp)
|
|
{
|
|
int i, others;
|
|
|
|
fprintf(fp, " xen_kdump_data: %s\n",
|
|
XEN_CORE_DUMPFILE() ? " " : "(unused)");
|
|
if (!XEN_CORE_DUMPFILE())
|
|
return;
|
|
fprintf(fp, " flags: %lx (", xkd->flags);
|
|
others = 0;
|
|
if (xkd->flags & KDUMP_P2M_INIT)
|
|
fprintf(fp, "%sKDUMP_P2M_INIT", others++ ? "|" : "");
|
|
if (xkd->flags & KDUMP_CR3)
|
|
fprintf(fp, "%sKDUMP_CR3", others++ ? "|" : "");
|
|
if (xkd->flags & KDUMP_MFN_LIST)
|
|
fprintf(fp, "%sKDUMP_MFN_LIST", others++ ? "|" : "");
|
|
fprintf(fp, ")\n");
|
|
fprintf(fp, " p2m_mfn: %lx\n",
|
|
xkd->p2m_mfn);
|
|
fprintf(fp, " cr3: %lx\n",
|
|
xkd->cr3);
|
|
fprintf(fp, " last_mfn_read: %lx\n",
|
|
xkd->last_mfn_read);
|
|
fprintf(fp, " last_pmd_read: %lx\n",
|
|
xkd->last_pmd_read);
|
|
fprintf(fp, " page: %lx\n",
|
|
(ulong)xkd->page);
|
|
fprintf(fp, " accesses: %ld\n",
|
|
xkd->accesses);
|
|
fprintf(fp, " cache_hits: %ld ",
|
|
xkd->cache_hits);
|
|
if (xkd->accesses)
|
|
fprintf(fp, "(%ld%%)",
|
|
xkd->cache_hits * 100 / xkd->accesses);
|
|
fprintf(fp, "\n p2m_frames: %d\n",
|
|
xkd->p2m_frames);
|
|
fprintf(fp, " xen_phys_start: %lx\n",
|
|
xkd->xen_phys_start);
|
|
fprintf(fp, " xen_major_version: %d\n",
|
|
xkd->xen_major_version);
|
|
fprintf(fp, " xen_minor_version: %d\n",
|
|
xkd->xen_minor_version);
|
|
fprintf(fp, " p2m_mfn_frame_list: %lx\n",
|
|
(ulong)xkd->p2m_mfn_frame_list);
|
|
for (i = 0; i < xkd->p2m_frames; i++)
|
|
fprintf(fp, "%lx ", xkd->p2m_mfn_frame_list[i]);
|
|
if (i) fprintf(fp, "\n");
|
|
}
|
|
|
|
void
|
|
process_xen_note(ulong type, void *data, size_t sz)
|
|
{
|
|
ulong *up = (ulong*) data;
|
|
unsigned words = sz / sizeof(ulong);
|
|
|
|
pc->flags |= XEN_CORE;
|
|
xkd->last_mfn_read = UNINITIALIZED;
|
|
xkd->last_pmd_read = UNINITIALIZED;
|
|
|
|
if (type == NT_XEN_KDUMP_CR3)
|
|
error(WARNING,
|
|
"obsolete Xen n_type: %lx (NT_XEN_KDUMP_CR3)\n\n",
|
|
type);
|
|
|
|
if (type == NT_XEN_KDUMP_CR3 && words == 1) {
|
|
xkd->flags |= KDUMP_CR3;
|
|
/*
|
|
* Use the first cr3 found.
|
|
*/
|
|
if (!xkd->cr3)
|
|
xkd->cr3 = *up;
|
|
} else {
|
|
xkd->flags |= KDUMP_MFN_LIST;
|
|
/*
|
|
* If already set, overridden with --pfm_mfn
|
|
*/
|
|
if (!xkd->p2m_mfn)
|
|
xkd->p2m_mfn = up[words-1];
|
|
if (words > 9 && !xkd->xen_phys_start)
|
|
xkd->xen_phys_start = up[words-2];
|
|
xkd->xen_major_version = up[0];
|
|
xkd->xen_minor_version = up[1];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Override the dom0 p2m mfn in the XEN_ELFNOTE_CRASH_INFO note
|
|
* in order to initiate a crash session of a guest kernel.
|
|
*/
|
|
void
|
|
xen_kdump_p2m_mfn(char *arg)
|
|
{
|
|
ulong value;
|
|
int errflag;
|
|
|
|
errflag = 0;
|
|
value = htol(arg, RETURN_ON_ERROR|QUIET, &errflag);
|
|
if (!errflag) {
|
|
xen_kdump_data.p2m_mfn = value;
|
|
if (CRASHDEBUG(1))
|
|
error(INFO,
|
|
"xen_kdump_data.p2m_mfn override: %lx\n",
|
|
value);
|
|
} else
|
|
error(WARNING, "invalid p2m_mfn argument: %s\n", arg);
|
|
}
|
|
|
|
/*
|
|
* Fujitsu dom0/HV sadump-generated dumpfile, which requires
|
|
* the --p2m_mfn command line argument.
|
|
*/
|
|
int
|
|
is_sadump_xen(void)
|
|
{
|
|
if (xen_kdump_data.p2m_mfn) {
|
|
if (!XEN_CORE_DUMPFILE()) {
|
|
pc->flags |= XEN_CORE;
|
|
xkd->last_mfn_read = UNINITIALIZED;
|
|
xkd->last_pmd_read = UNINITIALIZED;
|
|
xkd->flags |= KDUMP_MFN_LIST;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
set_xen_phys_start(char *arg)
|
|
{
|
|
ulong value;
|
|
int errflag = 0;
|
|
|
|
value = htol(arg, RETURN_ON_ERROR|QUIET, &errflag);
|
|
if (!errflag)
|
|
xen_kdump_data.xen_phys_start = value;
|
|
else
|
|
error(WARNING, "invalid xen_phys_start argument: %s\n", arg);
|
|
}
|
|
|
|
ulong
|
|
xen_phys_start(void)
|
|
{
|
|
return xkd->xen_phys_start;
|
|
}
|
|
|
|
int
|
|
xen_major_version(void)
|
|
{
|
|
return xkd->xen_major_version;
|
|
}
|
|
|
|
int
|
|
xen_minor_version(void)
|
|
{
|
|
return xkd->xen_minor_version;
|
|
}
|
|
|
|
struct xen_kdump_data *
|
|
get_xen_kdump_data(void)
|
|
{
|
|
return xkd;
|
|
}
|
|
|
|
/*
|
|
* Translate a xen domain's pseudo-physical address into the
|
|
* xen machine address. Since there's no compression involved,
|
|
* just the last phys_to_machine_mapping[] page read is cached,
|
|
* which essentially caches 1024 p2m translations.
|
|
*/
|
|
physaddr_t
|
|
xen_kdump_p2m(physaddr_t pseudo)
|
|
{
|
|
ulong pfn, mfn_frame;
|
|
ulong *mfnptr;
|
|
ulong mfn_idx, frame_idx;
|
|
physaddr_t paddr;
|
|
|
|
if (pc->curcmd_flags & XEN_MACHINE_ADDR)
|
|
return pseudo;
|
|
|
|
if (!(xkd->flags & KDUMP_P2M_INIT)) {
|
|
if (!machdep->xen_kdump_p2m_create)
|
|
error(FATAL,
|
|
"xen kdump dumpfiles not supported on this architecture\n");
|
|
|
|
if ((xkd->page =
|
|
(char *)malloc(PAGESIZE())) == NULL)
|
|
error(FATAL,
|
|
"cannot malloc xen kdump data page\n");
|
|
|
|
if (!machdep->xen_kdump_p2m_create(xkd))
|
|
error(FATAL,
|
|
"cannot create xen kdump pfn-to-mfn mapping\n");
|
|
|
|
xkd->flags |= KDUMP_P2M_INIT;
|
|
}
|
|
|
|
#ifdef IA64
|
|
return ia64_xen_kdump_p2m(xkd, pseudo);
|
|
#endif
|
|
|
|
xkd->accesses++;
|
|
|
|
pfn = (ulong)BTOP(pseudo);
|
|
mfn_idx = pfn / (PAGESIZE()/sizeof(ulong));
|
|
frame_idx = pfn % (PAGESIZE()/sizeof(ulong));
|
|
if (mfn_idx >= xkd->p2m_frames) {
|
|
if (CRASHDEBUG(8))
|
|
fprintf(fp, "xen_kdump_p2m: paddr/pfn: %llx/%lx: "
|
|
"mfn_idx nonexistent\n",
|
|
(ulonglong)pseudo, pfn);
|
|
return P2M_FAILURE;
|
|
}
|
|
mfn_frame = xkd->p2m_mfn_frame_list[mfn_idx];
|
|
|
|
if (mfn_frame == xkd->last_mfn_read)
|
|
xkd->cache_hits++;
|
|
else {
|
|
int res;
|
|
|
|
if (CRASHDEBUG(8))
|
|
fprintf(fp, "xen_kdump_p2m: paddr/pfn: %llx/%lx: "
|
|
"read mfn_frame: %llx\n",
|
|
(ulonglong)pseudo, pfn, PTOB(mfn_frame));
|
|
|
|
pc->curcmd_flags |= XEN_MACHINE_ADDR;
|
|
res = readmem((physaddr_t)PTOB(mfn_frame), PHYSADDR,
|
|
xkd->page, PAGESIZE(),
|
|
"xen_kdump_p2m mfn frame", RETURN_ON_ERROR);
|
|
pc->curcmd_flags &= ~XEN_MACHINE_ADDR;
|
|
|
|
if (!res)
|
|
return P2M_FAILURE;
|
|
}
|
|
|
|
xkd->last_mfn_read = mfn_frame;
|
|
|
|
mfnptr = ((ulong *)(xkd->page)) + frame_idx;
|
|
paddr = (physaddr_t)PTOB((ulonglong)(*mfnptr));
|
|
paddr |= PAGEOFFSET(pseudo);
|
|
|
|
if (CRASHDEBUG(7))
|
|
fprintf(fp,
|
|
"xen_kdump_p2m(%llx): mfn_idx: %ld frame_idx: %ld"
|
|
" mfn_frame: %lx mfn: %lx => %llx\n",
|
|
(ulonglong)pseudo, mfn_idx, frame_idx,
|
|
mfn_frame, *mfnptr, (ulonglong)paddr);
|
|
|
|
return paddr;
|
|
}
|