1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 /*
  26  * Copyright (c) 2012 by Delphix. All rights reserved.
  27  */
  28 
  29 #include <mdb/mdb_modapi.h>
  30 #include <mdb/mdb_ks.h>
  31 #include <mdb/mdb_ctf.h>
  32 #include <mdb/mdb_gelf.h>
  33 #include <mdb/mdb_target_impl.h>
  34 #include <mdb/mdb_kvm.h>
  35 #include <mdb/mdb.h>
  36 #include <xen/public/xen.h>
  37 #include <xen/public/arch-x86/xen.h>
  38 #include <errno.h>
  39 
  40 static mdb_ctf_id_t domain_type;
  41 
  42 /*
  43  * Some constants found in the non-public sched.h header file
  44  */
  45 #define MAX_EVTCHNS             NR_EVENT_CHANNELS
  46 #define EVTCHNS_PER_BUCKET      128
  47 #define NR_EVTCHN_BUCKETS       (MAX_EVTCHNS / EVTCHNS_PER_BUCKET)
  48 
  49 /*
  50  * "struct domain" is an internal Xen structure.  Rather than trying to
  51  * keep the mdb source in sync with Xen, we use CTF to extract the
  52  * interesting bits from the binary, and stash them in the structure
  53  * defined below.
  54  */
  55 typedef struct mdb_xpv_domain {
  56         short           domain_id;
  57         int             tot_pages;
  58         int             max_pages;
  59         int             xenheap_pages;
  60         ulong_t         domain_flags;
  61         char            is_hvm;
  62         struct vcpu     *vcpu[MAX_VIRT_CPUS];
  63         struct evtchn   *evtchn[NR_EVTCHN_BUCKETS];
  64         struct domain   *next_in_list;
  65 } mdb_xpv_domain_t;
  66 
  67 static uintptr_t
  68 get_dom0_addr()
  69 {
  70         GElf_Sym sym;
  71         uintptr_t addr;
  72 
  73         if ((mdb_lookup_by_obj(MDB_TGT_OBJ_EVERY, "dom0", &sym)) == 1) {
  74                 mdb_warn("can't find symbol 'dom0'");
  75                 return (0);
  76         }
  77 
  78         if (sym.st_size != sizeof (uintptr_t)) {
  79                 mdb_printf("Symbol 'dom0' found, but with the wrong size\n");
  80                 return (0);
  81         }
  82 
  83         if (mdb_vread(&addr, sym.st_size, sym.st_value) == -1) {
  84                 mdb_warn("can't read data for symbol 'dom0'");
  85                 return (0);
  86         }
  87 
  88         return (addr);
  89 }
  90 
  91 typedef struct domain_walk {
  92         uint_t dw_step;
  93 } domain_walk_t;
  94 
  95 int
  96 domain_walk_init(mdb_walk_state_t *wsp)
  97 {
  98         domain_walk_t *dwp;
  99 
 100         if (wsp->walk_addr == NULL)
 101                 if ((wsp->walk_addr = get_dom0_addr()) == NULL)
 102                         return (WALK_ERR);
 103 
 104         dwp = mdb_alloc(sizeof (domain_walk_t), UM_SLEEP);
 105         dwp->dw_step = FALSE;
 106         wsp->walk_data = dwp;
 107         return (WALK_NEXT);
 108 }
 109 
 110 int
 111 domain_walk_step(mdb_walk_state_t *wsp)
 112 {
 113         domain_walk_t *dwp = (domain_walk_t *)wsp->walk_data;
 114         mdb_xpv_domain_t dom;
 115         int status;
 116 
 117         if (wsp->walk_addr == NULL)
 118                 return (WALK_DONE);
 119 
 120         status = wsp->walk_callback(wsp->walk_addr, (void *)wsp->walk_addr,
 121             wsp->walk_cbdata);
 122 
 123         if (mdb_ctf_vread(&dom, "struct domain", "mdb_xpv_domain_t",
 124             wsp->walk_addr, 0) != 0)
 125                 return (WALK_ERR);
 126         wsp->walk_addr = (uintptr_t)dom.next_in_list;
 127 
 128         dwp->dw_step = TRUE;
 129         return (status);
 130 }
 131 
 132 void
 133 domain_walk_fini(mdb_walk_state_t *wsp)
 134 {
 135         domain_walk_t *dwp = (domain_walk_t *)wsp->walk_data;
 136 
 137         mdb_free(dwp, sizeof (domain_walk_t));
 138 }
 139 
 140 typedef struct vcpu_walk {
 141         uint_t vw_count;
 142         uint_t vw_step;
 143 } vcpu_walk_t;
 144 
 145 int
 146 vcpu_walk_init(mdb_walk_state_t *wsp)
 147 {
 148         vcpu_walk_t *vwp;
 149         uintptr_t off;
 150 
 151         if (wsp->walk_addr == NULL)
 152                 if ((wsp->walk_addr = get_dom0_addr()) == NULL)
 153                         return (WALK_ERR);
 154 
 155         if (mdb_ctf_offsetof(domain_type, "vcpu", &off)) {
 156                 mdb_warn("can't find per-domain vcpu information");
 157                 return (WALK_ERR);
 158         }
 159 
 160         wsp->walk_addr = wsp->walk_addr + (off / NBBY);
 161         vwp = mdb_alloc(sizeof (vcpu_walk_t), UM_SLEEP);
 162         vwp->vw_step = FALSE;
 163         vwp->vw_count = 0;
 164         wsp->walk_data = vwp;
 165         return (WALK_NEXT);
 166 }
 167 
 168 int
 169 vcpu_walk_step(mdb_walk_state_t *wsp)
 170 {
 171         vcpu_walk_t *vwp = (vcpu_walk_t *)wsp->walk_data;
 172         uintptr_t vcpu_ptr;
 173         int status;
 174 
 175         if (vwp->vw_count++ >= MAX_VIRT_CPUS)
 176                 return (WALK_DONE);
 177         if ((wsp->walk_addr == NULL) ||
 178             (mdb_vread(&vcpu_ptr, sizeof (uintptr_t), wsp->walk_addr) == -1) ||
 179             (vcpu_ptr == 0))
 180                 return (WALK_DONE);
 181 
 182         status = wsp->walk_callback(vcpu_ptr, (void *)vcpu_ptr,
 183             wsp->walk_cbdata);
 184 
 185         wsp->walk_addr = wsp->walk_addr + sizeof (uintptr_t);
 186         vwp->vw_step = TRUE;
 187         return (status);
 188 }
 189 
 190 void
 191 vcpu_walk_fini(mdb_walk_state_t *wsp)
 192 {
 193         vcpu_walk_t *vwp = (vcpu_walk_t *)wsp->walk_data;
 194 
 195         mdb_free(vwp, sizeof (vcpu_walk_t));
 196 }
 197 
 198 int
 199 domain(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 200 {
 201         mdb_xpv_domain_t dom;
 202         uintptr_t off, vcpu_addr, evtchn_addr;
 203 
 204         if (!mdb_ctf_type_valid(domain_type)) {
 205                 mdb_warn("Can't parse Xen domain info.\n");
 206                 return (DCMD_ERR);
 207         }
 208 
 209         if (!(flags & DCMD_ADDRSPEC)) {
 210                 if (mdb_walk_dcmd("domain", "domain", argc, argv) == -1) {
 211                         mdb_warn("can't walk domains");
 212                         return (DCMD_ERR);
 213                 }
 214                 return (DCMD_OK);
 215         }
 216 
 217         if (DCMD_HDRSPEC(flags))
 218                 mdb_printf("%?s %3s %8s %8s %8s %3s %?s %?s\n",
 219                     "ADDR", "ID", "TPAGES", "MPAGES", "FLAGS", "HVM",
 220                     "VCPU", "EVTCHN");
 221 
 222         if (mdb_ctf_vread(&dom, "struct domain", "mdb_xpv_domain_t", addr,
 223             0) != 0)
 224                 return (DCMD_ERR);
 225 
 226         if (mdb_ctf_offsetof(domain_type, "vcpu", &off)) {
 227                 mdb_warn("can't find per-domain vcpu information");
 228                 return (DCMD_ERR);
 229         }
 230         vcpu_addr = addr + (off / NBBY);
 231         if (mdb_ctf_offsetof(domain_type, "evtchn", &off)) {
 232                 mdb_warn("can't find per-domain event channel information");
 233                 return (DCMD_ERR);
 234         }
 235         evtchn_addr = addr + (off / NBBY);
 236         mdb_printf("%?lx %3d %8x %8x %8x %3d %?lx %?lx\n",
 237             addr, dom.domain_id, dom.tot_pages, dom.max_pages, dom.domain_flags,
 238             dom.is_hvm, vcpu_addr, evtchn_addr);
 239 
 240         return (DCMD_OK);
 241 }
 242 
 243 static const mdb_dcmd_t dcmds[] = {
 244         { "domain", ":", "display Xen domain info", domain },
 245         { NULL }
 246 };
 247 
 248 static const mdb_walker_t walkers[] = {
 249         { "domain", "walk list of Xen domains",
 250                 domain_walk_init, domain_walk_step, domain_walk_fini },
 251         { "vcpu", "walk a Xen domain's vcpus",
 252                 vcpu_walk_init, vcpu_walk_step, vcpu_walk_fini },
 253         { NULL }
 254 };
 255 
 256 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };
 257 
 258 typedef struct mdb_xpv_panic_info {
 259         int pi_version;
 260 } mdb_xpv_panic_info_t;
 261 
 262 const mdb_modinfo_t *
 263 _mdb_init(void)
 264 {
 265         uintptr_t pip;
 266         mdb_xpv_panic_info_t pi;
 267 
 268         if (mdb_readsym(&pip, sizeof (pip), "xpv_panic_info") == -1) {
 269                 mdb_warn("failed to read xpv panic_info pointer");
 270                 return (NULL);
 271         }
 272         if (mdb_ctf_vread(&pi, "struct panic_info", "mdb_xpv_panic_info_t",
 273             pip, 0) == -1)
 274                 return (NULL);
 275 
 276         if (pi.pi_version != PANIC_INFO_VERSION) {
 277                 mdb_warn("unrecognized hypervisor panic format");
 278                 return (NULL);
 279         }
 280 
 281         if (mdb_ctf_lookup_by_name("struct domain", &domain_type) != 0) {
 282                 mdb_warn("Can't parse Xen domain info: "
 283                     "'struct domain' not found.\n");
 284                 mdb_ctf_type_invalidate(&domain_type);
 285         }
 286 
 287         return (&modinfo);
 288 }