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) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25#include <mdb/mdb_modapi.h>
26#include <sys/sysmacros.h>
27#include <sys/sunddi.h>
28#include <sys/damap.h>
29#include <sys/damap_impl.h>
30
31#include "damap.h"
32
33void
34damap_help(void)
35{
36	mdb_printf("Print the damap at the address given.\n");
37	mdb_printf("\n");
38	mdb_printf("EXAMPLE: SCSI: To display the SCSI tgtmap damaps ");
39	mdb_printf("associated with a scsi HBA driver iport dip:\n");
40	mdb_printf("\n");
41	mdb_printf("::devbindings -q <driver_name>\n");
42	mdb_printf("\n");
43	mdb_printf("<iport-dip>::print struct dev_info devi_driver_data|");
44	mdb_printf("::print scsi_hba_tran_t tran_tgtmap|");
45	mdb_printf("::print impl_scsi_tgtmap_t ");
46	mdb_printf("tgtmap_dam[0] tgtmap_dam[1]|::damap\n");
47}
48
49static char *
50local_strdup(const char *s)
51{
52	if (s)
53		return (strcpy(mdb_alloc(strlen(s) + 1, UM_SLEEP), s));
54	else
55		return (NULL);
56}
57
58static void
59local_strfree(const char *s)
60{
61	if (s)
62		mdb_free((void *)s, strlen(s) + 1);
63}
64
65static void
66bitset_free(bitset_t *bs, int embedded)
67{
68	if (bs == NULL)
69		return;
70	if (bs->bs_set && bs->bs_words)
71		mdb_free(bs->bs_set, bs->bs_words * sizeof (ulong_t));
72	if (!embedded)
73		mdb_free(bs, sizeof (*bs));	/* not embedded, free */
74}
75
76static bitset_t *
77bitset_get(uintptr_t bsaddr)
78{
79	bitset_t	*bs;
80
81	bs = mdb_zalloc(sizeof (*bs), UM_SLEEP);
82	if (mdb_vread(bs, sizeof (*bs), bsaddr) == -1) {
83		mdb_warn("couldn't read bitset 0x%p", bsaddr);
84		bitset_free(bs, 0);
85		return (NULL);
86	}
87
88	bsaddr = (uintptr_t)bs->bs_set;
89	bs->bs_set = mdb_alloc(bs->bs_words * sizeof (ulong_t), UM_SLEEP);
90	if (mdb_vread(bs->bs_set,
91	    bs->bs_words * sizeof (ulong_t), bsaddr) == -1) {
92		mdb_warn("couldn't read bitset bs_set 0x%p", bsaddr);
93		bitset_free(bs, 0);
94		return (NULL);
95	}
96	return (bs);
97
98}
99
100static void
101damap_free(struct dam *dam, void **kdamda, int kdamda_n)
102{
103	int			i;
104	struct i_ddi_soft_state *ss;
105	dam_da_t		*da;
106
107	if (dam) {
108		/* free in dam_da_t softstate */
109		ss = (struct i_ddi_soft_state *)dam->dam_da;
110		if (ss) {
111			if (ss->n_items && ss->array) {
112				for (i = 0; i < ss->n_items; i++) {
113					da = ss->array[i];
114					if (da == NULL)
115						continue;
116					local_strfree(da->da_addr);
117					mdb_free(da, sizeof (*da));
118				}
119			}
120
121			mdb_free(ss, sizeof (*ss));
122		}
123
124		/* free dam_active/stable/report_set embedded in dam */
125		bitset_free(&dam->dam_report_set, 1);
126		bitset_free(&dam->dam_stable_set, 1);
127		bitset_free(&dam->dam_active_set, 1);
128
129		/* free dam_name */
130		local_strfree(dam->dam_name);
131
132		/* free dam */
133		mdb_free(dam, sizeof (*dam));
134	}
135
136	if (kdamda)
137		mdb_free(kdamda, kdamda_n * sizeof (void *));
138}
139
140/*
141 * The dam implementation uses a number of different abstractions. Given a
142 * pointer to a damap_t, this function make an mdb instantiation of the dam -
143 * many, but not all, of the different abstractions used in the dam
144 * implementation are also instantiated in mdb. This means that callers of
145 * damap_get can perform some (but not all) types of structure pointer
146 * traversals.
147 */
148struct dam *
149damap_get(uintptr_t damaddr, void ***pkdamda, int *pkdamda_n)
150{
151	/* variables that hold instantiation read from kernel */
152	struct dam		kdam;
153	char			kstring[MAXPATHLEN];
154	struct i_ddi_soft_state kss;
155	void			**kssarray = NULL;
156	int			array_sz = 0;
157
158	/* variables that hold mdb instantiation */
159	struct dam		*dam = NULL;
160	struct i_ddi_soft_state *ss;
161	bitset_t		*bs;
162	dam_da_t		*da;
163
164	int			i;
165
166	/* read kernel: dam */
167	if (mdb_vread(&kdam, sizeof (kdam), damaddr) == -1) {
168		mdb_warn("couldn't read dam 0x%p", damaddr);
169		goto err;
170	}
171
172	/* read kernel: dam->dam_name */
173	mdb_readstr(kstring, sizeof (kstring), (uintptr_t)kdam.dam_name);
174
175	/* read kernel: dam->dam_da (softstate) */
176	if (mdb_vread(&kss, sizeof (kss), (uintptr_t)kdam.dam_da) == -1) {
177		mdb_warn("couldn't read dam dam_da 0x%p",
178		    (uintptr_t)kdam.dam_da);
179		goto err;
180	}
181
182	/* read kernel ((struct i_ddi_soft_state *)dam->dam_da)->array */
183	array_sz = kss.n_items * sizeof (void *);
184	kssarray = mdb_alloc(array_sz, UM_SLEEP);
185	if (mdb_vread(kssarray, array_sz, (uintptr_t)kss.array) == -1) {
186		mdb_warn("couldn't read dam dam_da array 0x%p",
187		    (uintptr_t)kss.array);
188		goto err;
189	}
190
191	/*
192	 * Produce mdb instantiation of kernel data structures.
193	 *
194	 * Structure copy kdam to dam, then clear out pointers in dam (some
195	 * will be filled in by mdb instantiation code below).
196	 */
197	dam = mdb_zalloc(sizeof (*dam), UM_SLEEP);
198	*dam = kdam;
199	dam->dam_name = NULL;
200
201	dam->dam_active_set.bs_set = NULL;
202	dam->dam_stable_set.bs_set = NULL;
203	dam->dam_report_set.bs_set = NULL;
204
205	dam->dam_da = NULL;
206	/* dam_addr_hash, dam_taskqp, dam_kstatp left as kernel addresses */
207
208	/* fill in dam_name */
209	dam->dam_name = local_strdup(kstring);
210
211	/* fill in dam_active/stable/report_set embedded in the dam */
212	bs = bitset_get(damaddr + (offsetof(struct dam, dam_active_set)));
213	if (bs) {
214		dam->dam_active_set = *bs;
215		mdb_free(bs, sizeof (*bs));
216	}
217	bs = bitset_get(damaddr + (offsetof(struct dam, dam_stable_set)));
218	if (bs) {
219		dam->dam_stable_set = *bs;
220		mdb_free(bs, sizeof (*bs));
221	}
222	bs = bitset_get(damaddr + (offsetof(struct dam, dam_report_set)));
223	if (bs) {
224		dam->dam_report_set = *bs;
225		mdb_free(bs, sizeof (*bs));
226	}
227
228	/* fill in dam_da_t softstate */
229	ss = mdb_zalloc(sizeof (struct i_ddi_soft_state), UM_SLEEP);
230	*ss = kss;
231	ss->next = NULL;
232	ss->array = mdb_zalloc(array_sz, UM_SLEEP);
233	dam->dam_da = ss;
234	for (i = 0; i < kss.n_items; i++) {
235		if (kssarray[i] == NULL)
236			continue;
237		da = ss->array[i] = mdb_zalloc(sizeof (*da), UM_SLEEP);
238		if (mdb_vread(da, sizeof (*da), (uintptr_t)kssarray[i]) == -1) {
239			mdb_warn("couldn't read dam dam_da %d 0x%p", i,
240			    (uintptr_t)kss.array);
241			goto err;
242		}
243		/* da_nvl, da_ppriv_rpt, da_nvl_rpt left as kernel addresses */
244
245		/* read kernel: da->da_addr */
246		mdb_readstr(kstring, sizeof (kstring), (uintptr_t)da->da_addr);
247		da->da_addr = local_strdup(kstring);
248	}
249
250	/* return array of kernel dam_da_t pointers associated with each id */
251	*pkdamda = kssarray;
252	*pkdamda_n = array_sz / sizeof (void *);
253
254	/* return pointer to mdb instantiation of the dam */
255	return (dam);
256
257err:	damap_free(dam, kssarray, array_sz / sizeof (void *));
258	*pkdamda = NULL;
259	*pkdamda_n = 0;
260	return (NULL);
261}
262
263/*ARGSUSED*/
264static void
265damap_print(struct dam *dam, void **kdamda, int kdamda_n)
266{
267	struct i_ddi_soft_state	*ss;
268	dam_da_t		*da;
269	int			i;
270
271	mdb_printf("%s:\n", dam->dam_name);
272
273	ss = (struct i_ddi_soft_state *)dam->dam_da;
274	if (ss == NULL)
275		return;
276
277	if ((ss->n_items == 0) || (ss->array == NULL))
278		return;
279
280	mdb_printf("    #: %-20s [ASR] ref config-private   provider-private\n",
281	    "address");
282	for (i = 0; i < ss->n_items; i++) {
283		da = ss->array[i];
284		if (da == NULL)
285			continue;
286
287		/* Print index and address. */
288		mdb_printf("  %3d: %-20s [", i, da->da_addr);
289
290		/* Print shorthand of Active/Stable/Report set membership */
291		if (BT_TEST(dam->dam_active_set.bs_set, i))
292			mdb_printf("A");
293		else
294			mdb_printf(".");
295		if (BT_TEST(dam->dam_stable_set.bs_set, i))
296			mdb_printf("S");
297		else
298			mdb_printf(".");
299		if (BT_TEST(dam->dam_report_set.bs_set, i))
300			mdb_printf("R");
301		else
302			mdb_printf(".");
303
304		/* Print the reference count and priv */
305		mdb_printf("] %-3d %0?lx %0?lx\n",
306		    da->da_ref, da->da_cfg_priv, da->da_ppriv);
307
308		mdb_printf("       %p::print -ta dam_da_t\n", kdamda[i]);
309	}
310}
311
312/*ARGSUSED*/
313int
314damap(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
315{
316	struct dam	*dam;
317	void		**kdamda;
318	int		kdamda_n;
319
320	if (!(flags & DCMD_ADDRSPEC)) {
321		return (DCMD_ERR);
322	}
323
324	dam = damap_get(addr, &kdamda, &kdamda_n);
325	if (dam == NULL)
326		return (DCMD_ERR);
327
328	damap_print(dam, kdamda, kdamda_n);
329	damap_free(dam, kdamda, kdamda_n);
330	return (DCMD_OK);
331}
332