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