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