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
zid_lookup_cb(uintptr_t addr,const zone_t * zone,void * arg)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
zid2zone(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
zoneprt(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
zone_walk_init(mdb_walk_state_t * wsp)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
zone_walk_step(mdb_walk_state_t * wsp)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
zsd_walk_init(mdb_walk_state_t * wsp)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
zsd_walk_step(mdb_walk_state_t * wsp)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
zsd_print(uintptr_t addrp,const void * datap,void * privatep)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
zsd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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