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 (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright (c) 2012, Joyent, Inc. All rights reserved. 24 * Copyright 2025 Oxide Computer Company 25 */ 26 27 #include <mdb/mdb_param.h> 28 #include <mdb/mdb_modapi.h> 29 #include <mdb/mdb_ks.h> 30 31 #include "zone.h" 32 33 #include <stddef.h> 34 #include <sys/zone.h> 35 36 #define ZONE_NAMELEN 20 37 #ifdef _LP64 38 #define ZONE_PATHLEN 32 39 #else 40 #define ZONE_PATHLEN 40 41 #endif 42 43 /* 44 * Names corresponding to zone_status_t values in sys/zone.h 45 */ 46 char *zone_status_names[] = { 47 "uninitialized", /* ZONE_IS_UNINITIALIZED */ 48 "initialized", /* ZONE_IS_INITIALIZED */ 49 "ready", /* ZONE_IS_READY */ 50 "booting", /* ZONE_IS_BOOTING */ 51 "running", /* ZONE_IS_RUNNING */ 52 "shutting_down", /* ZONE_IS_SHUTTING_DOWN */ 53 "empty", /* ZONE_IS_EMPTY */ 54 "down", /* ZONE_IS_DOWN */ 55 "dying", /* ZONE_IS_DYING */ 56 "dead" /* ZONE_IS_DEAD */ 57 }; 58 59 static int 60 zid_lookup_cb(uintptr_t addr, const zone_t *zone, void *arg) 61 { 62 zoneid_t zid = *(uintptr_t *)arg; 63 if (zone->zone_id == zid) 64 mdb_printf("%p\n", addr); 65 66 return (WALK_NEXT); 67 } 68 69 /*ARGSUSED*/ 70 int 71 zid2zone(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 72 { 73 if (!(flags & DCMD_ADDRSPEC) || argc != 0) 74 return (DCMD_USAGE); 75 76 if (mdb_walk("zone", (mdb_walk_cb_t)zid_lookup_cb, &addr) == -1) { 77 mdb_warn("failed to walk zone"); 78 return (DCMD_ERR); 79 } 80 81 return (DCMD_OK); 82 } 83 84 int 85 zoneprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 86 { 87 zone_t zn; 88 char name[ZONE_NAMELEN]; 89 char path[ZONE_PATHLEN]; 90 int len; 91 uint_t vopt_given; 92 uint_t ropt_given; 93 94 if (argc > 2) 95 return (DCMD_USAGE); 96 97 if (!(flags & DCMD_ADDRSPEC)) { 98 if (mdb_walk_dcmd("zone", "zone", argc, argv) == -1) { 99 mdb_warn("can't walk zones"); 100 return (DCMD_ERR); 101 } 102 return (DCMD_OK); 103 } 104 105 /* 106 * Get the optional -r (reference counts) and -v (verbose output) 107 * arguments. 108 */ 109 vopt_given = FALSE; 110 ropt_given = FALSE; 111 if (argc > 0 && mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, 112 &vopt_given, 'r', MDB_OPT_SETBITS, TRUE, &ropt_given, NULL) != argc) 113 return (DCMD_USAGE); 114 115 /* 116 * -v can only be specified with -r. 117 */ 118 if (vopt_given == TRUE && ropt_given == FALSE) 119 return (DCMD_USAGE); 120 121 /* 122 * Print a table header, if necessary. 123 */ 124 if (DCMD_HDRSPEC(flags)) { 125 if (ropt_given == FALSE) 126 mdb_printf("%<u>%?s %6s %-13s %-20s %-s%</u>\n", 127 "ADDR", "ID", "STATUS", "NAME", "PATH"); 128 else 129 mdb_printf("%<u>%?s %6s %10s %10s %-20s%</u>\n", 130 "ADDR", "ID", "REFS", "CREFS", "NAME"); 131 } 132 133 /* 134 * Read the zone_t structure at the given address and read its name. 135 */ 136 if (mdb_vread(&zn, sizeof (zone_t), addr) == -1) { 137 mdb_warn("can't read zone_t structure at %p", addr); 138 return (DCMD_ERR); 139 } 140 len = mdb_readstr(name, ZONE_NAMELEN, (uintptr_t)zn.zone_name); 141 if (len > 0) { 142 if (len == ZONE_NAMELEN) 143 (void) strcpy(&name[len - 4], "..."); 144 } else { 145 (void) strcpy(name, "??"); 146 } 147 148 if (ropt_given == FALSE) { 149 char *statusp; 150 151 /* 152 * Default display 153 * Fetch the zone's path and print the results. 154 */ 155 len = mdb_readstr(path, ZONE_PATHLEN, 156 (uintptr_t)zn.zone_rootpath); 157 if (len > 0) { 158 if (len == ZONE_PATHLEN) 159 (void) strcpy(&path[len - 4], "..."); 160 } else { 161 (void) strcpy(path, "??"); 162 } 163 if (zn.zone_status >= ZONE_IS_UNINITIALIZED && zn.zone_status <= 164 ZONE_IS_DEAD) 165 statusp = zone_status_names[zn.zone_status]; 166 else 167 statusp = "???"; 168 mdb_printf("%0?p %6d %-13s %-20s %s\n", addr, zn.zone_id, 169 statusp, name, path); 170 } else { 171 /* 172 * Display the zone's reference counts. 173 * Display the zone's subsystem-specific reference counts if 174 * the user specified the '-v' option. 175 */ 176 mdb_printf("%0?p %6d %10u %10u %-20s\n", addr, zn.zone_id, 177 zn.zone_ref, zn.zone_cred_ref, name); 178 if (vopt_given == TRUE) { 179 GElf_Sym subsys_names_sym; 180 uintptr_t **zone_ref_subsys_names; 181 uint_t num_subsys; 182 uint_t n; 183 184 /* 185 * Read zone_ref_subsys_names from the kernel image. 186 */ 187 if (mdb_lookup_by_name("zone_ref_subsys_names", 188 &subsys_names_sym) != 0) { 189 mdb_warn("can't find zone_ref_subsys_names"); 190 return (DCMD_ERR); 191 } 192 if (subsys_names_sym.st_size != ZONE_REF_NUM_SUBSYS * 193 sizeof (char *)) { 194 mdb_warn("number of subsystems in target " 195 "differs from what mdb expects (mismatched" 196 " kernel versions?)"); 197 if (subsys_names_sym.st_size < 198 ZONE_REF_NUM_SUBSYS * sizeof (char *)) 199 num_subsys = subsys_names_sym.st_size / 200 sizeof (char *); 201 else 202 num_subsys = ZONE_REF_NUM_SUBSYS; 203 } else { 204 num_subsys = ZONE_REF_NUM_SUBSYS; 205 } 206 if ((zone_ref_subsys_names = mdb_alloc( 207 subsys_names_sym.st_size, UM_GC)) == NULL) { 208 mdb_warn("out of memory"); 209 return (DCMD_ERR); 210 } 211 if (mdb_readvar(zone_ref_subsys_names, 212 "zone_ref_subsys_names") == -1) { 213 mdb_warn("can't find zone_ref_subsys_names"); 214 return (DCMD_ERR); 215 } 216 217 /* 218 * Display each subsystem's reference count if it's 219 * nonzero. 220 */ 221 mdb_inc_indent(7); 222 for (n = 0; n < num_subsys; ++n) { 223 char subsys_name[16]; 224 225 /* 226 * Skip subsystems lacking outstanding 227 * references. 228 */ 229 if (zn.zone_subsys_ref[n] == 0) 230 continue; 231 232 /* 233 * Each subsystem's name must be read from 234 * the target's image. 235 */ 236 if (mdb_readstr(subsys_name, 237 sizeof (subsys_name), 238 (uintptr_t)zone_ref_subsys_names[n]) == 239 -1) { 240 mdb_warn("unable to read subsystem name" 241 " from zone_ref_subsys_names[%u]", 242 n); 243 return (DCMD_ERR); 244 } 245 mdb_printf("%15s: %10u\n", subsys_name, 246 zn.zone_subsys_ref[n]); 247 } 248 mdb_dec_indent(7); 249 } 250 } 251 return (DCMD_OK); 252 } 253 254 int 255 zone_walk_init(mdb_walk_state_t *wsp) 256 { 257 GElf_Sym sym; 258 259 if (wsp->walk_addr == 0) { 260 if (mdb_lookup_by_name("zone_active", &sym) == -1) { 261 mdb_warn("failed to find 'zone_active'"); 262 return (WALK_ERR); 263 } 264 wsp->walk_addr = (uintptr_t)sym.st_value; 265 } 266 if (mdb_layered_walk("list", wsp) == -1) { 267 mdb_warn("couldn't walk 'list'"); 268 return (WALK_ERR); 269 } 270 return (WALK_NEXT); 271 } 272 273 int 274 zone_walk_step(mdb_walk_state_t *wsp) 275 { 276 return (wsp->walk_callback(wsp->walk_addr, wsp->walk_layer, 277 wsp->walk_cbdata)); 278 } 279 280 int 281 zsd_walk_init(mdb_walk_state_t *wsp) 282 { 283 if (wsp->walk_addr == 0) { 284 mdb_warn("global walk not supported\n"); 285 return (WALK_ERR); 286 } 287 wsp->walk_addr += offsetof(struct zone, zone_zsd); 288 if (mdb_layered_walk("list", wsp) == -1) { 289 mdb_warn("couldn't walk 'list'"); 290 return (WALK_ERR); 291 } 292 return (WALK_NEXT); 293 } 294 295 int 296 zsd_walk_step(mdb_walk_state_t *wsp) 297 { 298 return (wsp->walk_callback(wsp->walk_addr, wsp->walk_layer, 299 wsp->walk_cbdata)); 300 } 301 302 /* 303 * Helper structure used when walking ZSD entries via zsd(). 304 */ 305 struct zsd_cb_data { 306 uint_t keygiven; /* Was a key specified (are we */ 307 /* searching for a specific ZSD */ 308 /* entry)? */ 309 zone_key_t key; /* Key of ZSD for which we're looking */ 310 uint_t found; /* Was the specific ZSD entry found? */ 311 uint_t voptgiven; /* Display verbose information? */ 312 }; 313 314 /* 315 * Helper function for zsd() that displays information from a single ZSD struct. 316 * 'datap' must point to a valid zsd_cb_data struct. 317 */ 318 /* ARGSUSED */ 319 static int 320 zsd_print(uintptr_t addrp, const void * datap, void * privatep) 321 { 322 struct zsd_entry entry; 323 struct zsd_cb_data *cbdp; 324 325 if (mdb_vread(&entry, sizeof (entry), addrp) == -1) { 326 mdb_warn("couldn't read zsd_entry at %p", addrp); 327 return (WALK_ERR); 328 } 329 cbdp = (struct zsd_cb_data *)privatep; 330 331 /* 332 * Are we looking for a single entry specified by a key? Then make sure 333 * that the current ZSD's key is what we're looking for. 334 */ 335 if (cbdp->keygiven == TRUE && cbdp->key != entry.zsd_key) 336 return (WALK_NEXT); 337 338 mdb_printf("%?x %0?p %8x\n", entry.zsd_key, entry.zsd_data, 339 entry.zsd_flags); 340 if (cbdp->voptgiven == TRUE) 341 mdb_printf(" Create CB: %a\n Shutdown CB: %a\n" 342 " Destroy CB: %a\n", entry.zsd_create, 343 entry.zsd_shutdown, entry.zsd_destroy); 344 if (cbdp->keygiven == TRUE) { 345 cbdp->found = TRUE; 346 return (WALK_DONE); 347 } 348 return (WALK_NEXT); 349 } 350 351 int 352 zsd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 353 { 354 zone_t zone; 355 const mdb_arg_t *argp; 356 int argcindex; 357 struct zsd_cb_data cbd; 358 char name[ZONE_NAMELEN]; 359 int len; 360 361 /* 362 * Walk all zones if necessary. 363 */ 364 if (argc > 2) 365 return (DCMD_USAGE); 366 if ((flags & DCMD_ADDRSPEC) == 0) { 367 if (mdb_walk_dcmd("zone", "zsd", argc, argv) == -1) { 368 mdb_warn("failed to walk zone\n"); 369 return (DCMD_ERR); 370 } 371 return (DCMD_OK); 372 } 373 374 /* 375 * Make sure a zone_t can be read from the specified address. 376 */ 377 if (mdb_vread(&zone, sizeof (zone), addr) == -1) { 378 mdb_warn("couldn't read zone_t at %p", (void *)addr); 379 return (DCMD_ERR); 380 } 381 382 /* 383 * Get the optional arguments (key or -v or both). Note that 384 * mdb_getopts() will not parse a key argument because it is not 385 * preceded by an option letter. We'll get around this by requiring 386 * that all options precede the optional key argument. 387 */ 388 cbd.keygiven = FALSE; 389 cbd.voptgiven = FALSE; 390 if (argc > 0 && (argcindex = mdb_getopts(argc, argv, 'v', 391 MDB_OPT_SETBITS, TRUE, &cbd.voptgiven, NULL)) != argc) { 392 /* 393 * No options may appear after the key. 394 */ 395 if (argcindex != argc - 1) 396 return (DCMD_USAGE); 397 398 /* 399 * The missed argument should be a key. 400 */ 401 argp = &argv[argcindex]; 402 cbd.key = (zone_key_t)mdb_argtoull(argp); 403 cbd.keygiven = TRUE; 404 cbd.found = FALSE; 405 } 406 407 /* 408 * Prepare to output the specified zone's ZSD information. 409 */ 410 if (DCMD_HDRSPEC(flags)) 411 mdb_printf("%<u>%-20s %?s %?s %8s%</u>\n", "ZONE", "KEY", 412 "VALUE", "FLAGS"); 413 len = mdb_readstr(name, ZONE_NAMELEN, (uintptr_t)zone.zone_name); 414 if (len > 0) { 415 if (len == ZONE_NAMELEN) 416 (void) strcpy(&name[len - 4], "..."); 417 } else { 418 (void) strcpy(name, "??"); 419 } 420 mdb_printf("%-20s ", name); 421 422 /* 423 * Display the requested ZSD entries. 424 */ 425 mdb_inc_indent(21); 426 if (mdb_pwalk("zsd", zsd_print, &cbd, addr) != 0) { 427 mdb_warn("failed to walk zsd\n"); 428 mdb_dec_indent(21); 429 return (DCMD_ERR); 430 } 431 if (cbd.keygiven == TRUE && cbd.found == FALSE) { 432 mdb_printf("no corresponding ZSD entry found\n"); 433 mdb_dec_indent(21); 434 return (DCMD_ERR); 435 } 436 mdb_dec_indent(21); 437 return (DCMD_OK); 438 } 439