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 == 0) 101 if ((wsp->walk_addr = get_dom0_addr()) == 0) 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 == 0) 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 == 0) 152 if ((wsp->walk_addr = get_dom0_addr()) == 0) 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 == 0) || 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 } 289