mdb_kb.c revision a576ab5b6e08c47732b3dedca9eaa8a8cbb85720
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* KVM backend for hypervisor domain dumps. We don't use libkvm for
* such dumps, since they do not have a namelist file or the typical
* dump structures we expect to aid bootstrapping. Instead, we
* bootstrap based upon a debug_info structure at a known VA, using the
* guest's own page tables to resolve to physical addresses, and
* construct the namelist in a manner similar to ksyms_snapshot().
*
* Note that there are two formats understood by this module: the older,
* ad hoc format, which we call 'core' within this file, and an
* ELF-based format, known as 'elf'.
*
* We only support the older format generated on Solaris dom0: before we
* fixed it, core dump files were broken whenever a PFN didn't map a
* real MFN (!).
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <strings.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <gelf.h>
#include <errno.h>
#include <sys/debug_info.h>
#include <sys/kobj_impl.h>
#include <sys/sysmacros.h>
#include <sys/privmregs.h>
#include <mdb/mdb_target_impl.h>
#define XKB_SHDR_NULL 0
#define XKB_SHDR_SYMTAB 1
#define XKB_SHDR_STRTAB 2
#define XKB_SHDR_SHSTRTAB 3
#define XKB_SHDR_NUM 4
#define XKB_WALK_LOCAL 0x1
#define XKB_WALK_GLOBAL 0x2
#define XKB_WALK_STR 0x4
#if defined(__i386)
#define DEBUG_INFO 0xf4bff000
#define DEBUG_INFO 0xfffffffffb7ff000
#endif
#define PAGE_SIZE 0x1000
#define PAGE_SHIFT 12
#define PT_PADDR 0x000ffffffffff000ull
#define PT_VALID 0x1
#define XC_CORE_MAGIC 0xF00FEBED
#define XC_CORE_MAGIC_HVM 0xF00FEBEE
typedef struct xc_core_header {
unsigned int xch_magic;
unsigned int xch_nr_vcpus;
unsigned int xch_nr_pages;
unsigned int xch_ctxt_offset;
unsigned int xch_index_offset;
unsigned int xch_pages_offset;
struct xc_elf_header {
};
struct xc_elf_version {
};
/*
* Either an old-style (3.0.4) core format, or the ELF format.
*/
typedef enum {
XKB_FORMAT_UNKNOWN = 0,
XKB_FORMAT_CORE = 1,
XKB_FORMAT_ELF = 2
} xkb_type_t;
typedef struct mfn_map {
char *mm_map;
} mfn_map_t;
typedef struct mmu_info {
} mmu_info_t;
typedef struct xkb_core {
void *xc_p2m_buf;
} xkb_core_t;
typedef struct xkb_elf {
struct xc_elf_header xe_hdr;
struct xc_elf_version xe_version;
} xkb_elf_t;
typedef struct xkb {
char *xkb_path;
int xkb_fd;
int xkb_is_pae;
struct vcpu_guest_context *xkb_vcpus;
char *xkb_pages;
char *xkb_namelist;
} xkb_t;
static const char xkb_shstrtab[] = "\0.symtab\0.strtab\0.shstrtab\0";
typedef struct xkb_namelist {
char shstrings[sizeof (xkb_shstrtab)];
static int xkb_build_ksyms(xkb_t *);
/*
* Jump through the hoops we need to to correctly identify a core file
* of either the old or new format.
*/
int
{
char *pos;
int ret = 0;
int fd;
return (-1);
return (0);
}
*longmode = 0;
/*
* Indeed.
*/
#ifdef _LP64
if (sizeof (struct vcpu_guest_context) *
*longmode = 1;
#else
if (sizeof (struct vcpu_guest_context) *
*longmode = 1;
#endif /* _LP64 */
return (1);
}
return (-1);
goto out;
goto out;
goto out;
struct xc_elf_version *vers;
/* LINTED - alignment */
char *desc;
char *name;
continue;
/*
* The contents of this struct differ between 32 and 64
* bit; however, not until past the 'xev_capabilities'
* member, so we can just about get away with this.
*/
/* LINTED - alignment */
*longmode = 1;
*longmode = 0;
} else {
mdb_warn("couldn't derive word size of dump; "
"assuming 64-bit");
*longmode = 1;
}
}
ret = 1;
out:
return (ret);
}
static void *
{
return (NULL);
}
static int
{
size_t i;
for (i = 0; i <= xkb->xkb_max_pfn; i++) {
}
UM_SLEEP);
for (i = 0; i <= xkb->xkb_max_mfn; i++)
for (i = 0; i <= xkb->xkb_max_pfn; i++) {
}
return (1);
}
/*
* With FORMAT_CORE, we can use the table in the dump file directly.
* Just to make things fun, they've not page-aligned the p2m table.
*/
static int
{
/* LINTED - alignment */
return (0);
}
/* LINTED - alignment */
PAGE_OFFSET(boff));
return (1);
}
/*
* With FORMAT_ELF, we have a set of <pfn,mfn> pairs, which we convert
* into a linear array indexed by pfn for convenience. We also need to
* track the mapping between mfn and the offset in the file: a pfn with
* no mfn will not appear in the core file.
*/
static int
{
size_t i;
struct elf_p2m {
} *p2m;
return (0);
}
return (0);
}
for (i = 0; i < xkb->xkb_nr_pages; i++) {
}
for (i = 0; i <= xkb->xkb_max_pfn; i++) {
}
for (i = 0; i < xkb->xkb_nr_pages; i++) {
}
return (1);
}
/*
* Return the MFN of the top-level page table for the given as.
*/
static mfn_t
{
return (MFN_INVALID);
&htablep))
return (MFN_INVALID);
&pfn))
return (MFN_INVALID);
return (MFN_INVALID);
}
static ssize_t
{
return (-1);
while (left) {
if (!phys) {
if (mfn == MFN_INVALID)
return (-1);
} else {
return (-1);
if (mfn == MFN_INVALID)
return (-1);
}
/*
* If we're windowed then pread() is much faster.
*/
if (windowed) {
int ret;
if (off == ~1ULL)
return (-1);
if (ret == -1)
return (-1);
} else {
return (-1);
}
}
return (size);
}
static ssize_t
{
}
static ssize_t
{
}
static ssize_t
{
}
static int
{
sizeof (uintptr_t))
return (0);
return (1);
}
static char *
{
size_t i;
for (i = 0; i < 1024; i++) {
return (NULL);
}
if (str[i] == '\0')
break;
}
if (i == 1024) {
return (NULL);
}
return (str);
}
static offset_t
{
return (-1ULL);
}
static offset_t
{
return (-1ULL);
if (pfn == PFN_INVALID)
return (-1ULL);
}
static char *
{
if (windowed) {
}
return (NULL);
return (NULL);
} else {
return (NULL);
if (pfn == PFN_INVALID)
return (NULL);
}
}
static mfn_t
{
/* LINTED - alignment */
} else {
/* LINTED - alignment */
}
return (MFN_INVALID);
/* XXX: doesn't do large pages */
return (pte >> PAGE_SHIFT);
}
/*
* Resolve the given VA into an MFN, using the provided mfn as a top-level page
* table.
*/
static mfn_t
{
char *tmp;
return (MFN_INVALID);
return (MFN_INVALID);
if (level == 0)
break;
}
return (mfn);
}
static int
{
sizeof (struct module))
return (0);
return (0);
return (0);
return (0);
return (1);
}
static int
{
size_t i;
for (i = 0; i < sym_count; i++) {
char *name;
int type = XKB_WALK_GLOBAL;
return (0);
}
if (types & XKB_WALK_STR) {
}
}
}
return (1);
}
static int
{
/*
* empty first symbol
*/
if (types & XKB_WALK_LOCAL) {
}
if (types & XKB_WALK_STR) {
**buf = '\0';
(*buf)++;
}
}
for (;;) {
if (!xkb_read_word(xkb,
return (0);
goto next;
return (0);
goto next;
return (0);
next:
if (!xkb_read_word(xkb,
return (0);
break;
/*
* Try and prevent us looping forever if we have a broken list.
*/
if (--max_iter == 0)
break;
}
return (1);
}
/*
* Userspace equivalent of ksyms_snapshot(). Since we don't have a namelist
* file for hypervisor images, we fabricate one here using code similar
*/
static int
{
char *buf;
return (0);
return (0);
== NULL)
return (0);
/* LINTED - alignment */
return (0);
return (0);
return (0);
return (0);
return (1);
}
static xkb_t *
{
}
/*
* With FORMAT_CORE, all pages are in the dump (non-existing
* ones are zeroed out).
*/
/*
* Try to map all the data pages. If we can't, fall back to the
*/
/*
* We'd like to adapt for correctness' sake, but we have no way of
* detecting a PAE guest, since cr4 writes are disallowed.
*/
if (!xkb_map_p2m(xkb))
return (NULL);
return (xkb);
}
static xkb_t *
{
char *notes;
char *pos;
return (xkb);
}
return (xkb);
return (xkb);
/*
* Now we know this is indeed a hypervisor core dump, even if
* it's corrupted.
*/
/* LINTED - alignment */
char *desc;
char *name;
break;
"XEN_ELFNOTE_DUMPCORE_HEADER\n"));
}
sizeof (struct xc_elf_header));
break;
"XEN_ELFNOTE_DUMPCORE_XEN_VERSION\n"));
}
sizeof (struct xc_elf_version));
break;
/* LINTED - alignment */
if ((vers >> 32) != 0) {
"version %d (expected 0)\n",
(int)(vers >> 32)));
}
mdb_warn("unexpected dump minor number "
"version %d (expected 1)\n",
(int)(vers & 0xffffffff));
}
break;
default:
mdb_warn("unknown ELF note %d(%s)\n",
break;
}
}
}
"x86_32p") != NULL);
/*
* Try to map all the data pages. If we can't, fall back to the
*/
if (!xkb_build_p2m(xkb))
return (NULL);
return (xkb);
}
static void
{
#if defined(__amd64)
if (xkb->xkb_is_pae) {
} else {
}
#endif
}
/*ARGSUSED*/
xkb_t *
{
size_t i;
for (i = 0; i < 4; i++)
return (NULL);
if (!xkb_open_core(xkb))
return (NULL);
}
if (!xkb_build_m2p(xkb))
return (NULL);
}
}
if (!xkb_build_ksyms(xkb))
return (xkb);
}
int
{
size_t i;
return (0);
}
} else {
for (i = 0; i < 4; i++) {
if (addr != (char *)MAP_FAILED)
}
}
}
sz = sizeof (struct vcpu_guest_context) *
}
}
return (0);
}
/*ARGSUSED*/
static mdb_io_t *
{
return (io);
}
{
return (-1ULL);
return (-1ULL);
| PAGE_OFFSET(addr));
}
static int
{
struct vcpu_guest_context *vcpu;
struct cpu_user_regs *ur;
return (-1);
}
#ifdef __amd64
#else
#endif
return (0);
}
static mdb_kb_ops_t xpv_kb_ops = {
.kb_getmregs = (int (*)())xkb_getmregs
};
mdb_kb_ops(void)
{
return (&xpv_kb_ops);
}
const mdb_modinfo_t *
_mdb_init(void)
{
return (&modinfo);
}
void
_mdb_fini(void)
{
}