1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate /*
30*0Sstevel@tonic-gate  * driver for accessing kernel devinfo tree.
31*0Sstevel@tonic-gate  */
32*0Sstevel@tonic-gate #include <sys/types.h>
33*0Sstevel@tonic-gate #include <sys/pathname.h>
34*0Sstevel@tonic-gate #include <sys/debug.h>
35*0Sstevel@tonic-gate #include <sys/autoconf.h>
36*0Sstevel@tonic-gate #include <sys/conf.h>
37*0Sstevel@tonic-gate #include <sys/file.h>
38*0Sstevel@tonic-gate #include <sys/kmem.h>
39*0Sstevel@tonic-gate #include <sys/modctl.h>
40*0Sstevel@tonic-gate #include <sys/stat.h>
41*0Sstevel@tonic-gate #include <sys/ddi.h>
42*0Sstevel@tonic-gate #include <sys/sunddi.h>
43*0Sstevel@tonic-gate #include <sys/sunldi_impl.h>
44*0Sstevel@tonic-gate #include <sys/sunndi.h>
45*0Sstevel@tonic-gate #include <sys/esunddi.h>
46*0Sstevel@tonic-gate #include <sys/sunmdi.h>
47*0Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
48*0Sstevel@tonic-gate #include <sys/ndi_impldefs.h>
49*0Sstevel@tonic-gate #include <sys/mdi_impldefs.h>
50*0Sstevel@tonic-gate #include <sys/devinfo_impl.h>
51*0Sstevel@tonic-gate #include <sys/thread.h>
52*0Sstevel@tonic-gate #include <sys/modhash.h>
53*0Sstevel@tonic-gate #include <sys/bitmap.h>
54*0Sstevel@tonic-gate #include <util/qsort.h>
55*0Sstevel@tonic-gate #include <sys/disp.h>
56*0Sstevel@tonic-gate #include <sys/kobj.h>
57*0Sstevel@tonic-gate #include <sys/crc32.h>
58*0Sstevel@tonic-gate 
59*0Sstevel@tonic-gate 
60*0Sstevel@tonic-gate #ifdef DEBUG
61*0Sstevel@tonic-gate static int di_debug;
62*0Sstevel@tonic-gate #define	dcmn_err(args) if (di_debug >= 1) cmn_err args
63*0Sstevel@tonic-gate #define	dcmn_err2(args) if (di_debug >= 2) cmn_err args
64*0Sstevel@tonic-gate #define	dcmn_err3(args) if (di_debug >= 3) cmn_err args
65*0Sstevel@tonic-gate #else
66*0Sstevel@tonic-gate #define	dcmn_err(args) /* nothing */
67*0Sstevel@tonic-gate #define	dcmn_err2(args) /* nothing */
68*0Sstevel@tonic-gate #define	dcmn_err3(args) /* nothing */
69*0Sstevel@tonic-gate #endif
70*0Sstevel@tonic-gate 
71*0Sstevel@tonic-gate /*
72*0Sstevel@tonic-gate  * We partition the space of devinfo minor nodes equally between the full and
73*0Sstevel@tonic-gate  * unprivileged versions of the driver.  The even-numbered minor nodes are the
74*0Sstevel@tonic-gate  * full version, while the odd-numbered ones are the read-only version.
75*0Sstevel@tonic-gate  */
76*0Sstevel@tonic-gate static int di_max_opens = 32;
77*0Sstevel@tonic-gate 
78*0Sstevel@tonic-gate #define	DI_FULL_PARENT		0
79*0Sstevel@tonic-gate #define	DI_READONLY_PARENT	1
80*0Sstevel@tonic-gate #define	DI_NODE_SPECIES		2
81*0Sstevel@tonic-gate #define	DI_UNPRIVILEGED_NODE(x)	(((x) % 2) != 0)
82*0Sstevel@tonic-gate 
83*0Sstevel@tonic-gate #define	IOC_IDLE	0	/* snapshot ioctl states */
84*0Sstevel@tonic-gate #define	IOC_SNAP	1	/* snapshot in progress */
85*0Sstevel@tonic-gate #define	IOC_DONE	2	/* snapshot done, but not copied out */
86*0Sstevel@tonic-gate #define	IOC_COPY	3	/* copyout in progress */
87*0Sstevel@tonic-gate 
88*0Sstevel@tonic-gate /*
89*0Sstevel@tonic-gate  * Keep max alignment so we can move snapshot to different platforms
90*0Sstevel@tonic-gate  */
91*0Sstevel@tonic-gate #define	DI_ALIGN(addr)	((addr + 7l) & ~7l)
92*0Sstevel@tonic-gate 
93*0Sstevel@tonic-gate /*
94*0Sstevel@tonic-gate  * To avoid wasting memory, make a linked list of memory chunks.
95*0Sstevel@tonic-gate  * Size of each chunk is buf_size.
96*0Sstevel@tonic-gate  */
97*0Sstevel@tonic-gate struct di_mem {
98*0Sstevel@tonic-gate 	struct di_mem *next;	/* link to next chunk */
99*0Sstevel@tonic-gate 	char *buf;		/* contiguous kernel memory */
100*0Sstevel@tonic-gate 	size_t buf_size;	/* size of buf in bytes */
101*0Sstevel@tonic-gate 	devmap_cookie_t cook;	/* cookie from ddi_umem_alloc */
102*0Sstevel@tonic-gate };
103*0Sstevel@tonic-gate 
104*0Sstevel@tonic-gate /*
105*0Sstevel@tonic-gate  * This is a stack for walking the tree without using recursion.
106*0Sstevel@tonic-gate  * When the devinfo tree height is above some small size, one
107*0Sstevel@tonic-gate  * gets watchdog resets on sun4m.
108*0Sstevel@tonic-gate  */
109*0Sstevel@tonic-gate struct di_stack {
110*0Sstevel@tonic-gate 	void		*offset[MAX_TREE_DEPTH];
111*0Sstevel@tonic-gate 	struct dev_info *dip[MAX_TREE_DEPTH];
112*0Sstevel@tonic-gate 	int		circ[MAX_TREE_DEPTH];
113*0Sstevel@tonic-gate 	int		depth;	/* depth of current node to be copied */
114*0Sstevel@tonic-gate };
115*0Sstevel@tonic-gate 
116*0Sstevel@tonic-gate #define	TOP_OFFSET(stack)	\
117*0Sstevel@tonic-gate 	((di_off_t *)(stack)->offset[(stack)->depth - 1])
118*0Sstevel@tonic-gate #define	TOP_NODE(stack)		\
119*0Sstevel@tonic-gate 	((stack)->dip[(stack)->depth - 1])
120*0Sstevel@tonic-gate #define	PARENT_OFFSET(stack)	\
121*0Sstevel@tonic-gate 	((di_off_t *)(stack)->offset[(stack)->depth - 2])
122*0Sstevel@tonic-gate #define	EMPTY_STACK(stack)	((stack)->depth == 0)
123*0Sstevel@tonic-gate #define	POP_STACK(stack)	{ \
124*0Sstevel@tonic-gate 	ndi_devi_exit((dev_info_t *)TOP_NODE(stack), \
125*0Sstevel@tonic-gate 		(stack)->circ[(stack)->depth - 1]); \
126*0Sstevel@tonic-gate 	((stack)->depth--); \
127*0Sstevel@tonic-gate }
128*0Sstevel@tonic-gate #define	PUSH_STACK(stack, node, offp)	{ \
129*0Sstevel@tonic-gate 	ASSERT(node != NULL); \
130*0Sstevel@tonic-gate 	ndi_devi_enter((dev_info_t *)node, &(stack)->circ[(stack)->depth]); \
131*0Sstevel@tonic-gate 	(stack)->dip[(stack)->depth] = (node); \
132*0Sstevel@tonic-gate 	(stack)->offset[(stack)->depth] = (void *)(offp); \
133*0Sstevel@tonic-gate 	((stack)->depth)++; \
134*0Sstevel@tonic-gate }
135*0Sstevel@tonic-gate 
136*0Sstevel@tonic-gate #define	DI_ALL_PTR(s)	((struct di_all *)di_mem_addr((s), 0))
137*0Sstevel@tonic-gate 
138*0Sstevel@tonic-gate /*
139*0Sstevel@tonic-gate  * With devfs, the device tree has no global locks. The device tree is
140*0Sstevel@tonic-gate  * dynamic and dips may come and go if they are not locked locally. Under
141*0Sstevel@tonic-gate  * these conditions, pointers are no longer reliable as unique IDs.
142*0Sstevel@tonic-gate  * Specifically, these pointers cannot be used as keys for hash tables
143*0Sstevel@tonic-gate  * as the same devinfo structure may be freed in one part of the tree only
144*0Sstevel@tonic-gate  * to be allocated as the structure for a different device in another
145*0Sstevel@tonic-gate  * part of the tree. This can happen if DR and the snapshot are
146*0Sstevel@tonic-gate  * happening concurrently.
147*0Sstevel@tonic-gate  * The following data structures act as keys for devinfo nodes and
148*0Sstevel@tonic-gate  * pathinfo nodes.
149*0Sstevel@tonic-gate  */
150*0Sstevel@tonic-gate 
151*0Sstevel@tonic-gate enum di_ktype {
152*0Sstevel@tonic-gate 	DI_DKEY = 1,
153*0Sstevel@tonic-gate 	DI_PKEY = 2
154*0Sstevel@tonic-gate };
155*0Sstevel@tonic-gate 
156*0Sstevel@tonic-gate struct di_dkey {
157*0Sstevel@tonic-gate 	dev_info_t	*dk_dip;
158*0Sstevel@tonic-gate 	major_t		dk_major;
159*0Sstevel@tonic-gate 	int		dk_inst;
160*0Sstevel@tonic-gate 	dnode_t		dk_nodeid;
161*0Sstevel@tonic-gate };
162*0Sstevel@tonic-gate 
163*0Sstevel@tonic-gate struct di_pkey {
164*0Sstevel@tonic-gate 	mdi_pathinfo_t	*pk_pip;
165*0Sstevel@tonic-gate 	char		*pk_path_addr;
166*0Sstevel@tonic-gate 	dev_info_t	*pk_client;
167*0Sstevel@tonic-gate 	dev_info_t	*pk_phci;
168*0Sstevel@tonic-gate };
169*0Sstevel@tonic-gate 
170*0Sstevel@tonic-gate struct di_key {
171*0Sstevel@tonic-gate 	enum di_ktype	k_type;
172*0Sstevel@tonic-gate 	union {
173*0Sstevel@tonic-gate 		struct di_dkey dkey;
174*0Sstevel@tonic-gate 		struct di_pkey pkey;
175*0Sstevel@tonic-gate 	} k_u;
176*0Sstevel@tonic-gate };
177*0Sstevel@tonic-gate 
178*0Sstevel@tonic-gate 
179*0Sstevel@tonic-gate struct i_lnode;
180*0Sstevel@tonic-gate 
181*0Sstevel@tonic-gate typedef struct i_link {
182*0Sstevel@tonic-gate 	/*
183*0Sstevel@tonic-gate 	 * If a di_link struct representing this i_link struct makes it
184*0Sstevel@tonic-gate 	 * into the snapshot, then self will point to the offset of
185*0Sstevel@tonic-gate 	 * the di_link struct in the snapshot
186*0Sstevel@tonic-gate 	 */
187*0Sstevel@tonic-gate 	di_off_t	self;
188*0Sstevel@tonic-gate 
189*0Sstevel@tonic-gate 	int		spec_type;	/* block or char access type */
190*0Sstevel@tonic-gate 	struct i_lnode	*src_lnode;	/* src i_lnode */
191*0Sstevel@tonic-gate 	struct i_lnode	*tgt_lnode;	/* tgt i_lnode */
192*0Sstevel@tonic-gate 	struct i_link	*src_link_next;	/* next src i_link /w same i_lnode */
193*0Sstevel@tonic-gate 	struct i_link	*tgt_link_next;	/* next tgt i_link /w same i_lnode */
194*0Sstevel@tonic-gate } i_link_t;
195*0Sstevel@tonic-gate 
196*0Sstevel@tonic-gate typedef struct i_lnode {
197*0Sstevel@tonic-gate 	/*
198*0Sstevel@tonic-gate 	 * If a di_lnode struct representing this i_lnode struct makes it
199*0Sstevel@tonic-gate 	 * into the snapshot, then self will point to the offset of
200*0Sstevel@tonic-gate 	 * the di_lnode struct in the snapshot
201*0Sstevel@tonic-gate 	 */
202*0Sstevel@tonic-gate 	di_off_t	self;
203*0Sstevel@tonic-gate 
204*0Sstevel@tonic-gate 	/*
205*0Sstevel@tonic-gate 	 * used for hashing and comparing i_lnodes
206*0Sstevel@tonic-gate 	 */
207*0Sstevel@tonic-gate 	int		modid;
208*0Sstevel@tonic-gate 
209*0Sstevel@tonic-gate 	/*
210*0Sstevel@tonic-gate 	 * public information describing a link endpoint
211*0Sstevel@tonic-gate 	 */
212*0Sstevel@tonic-gate 	struct di_node	*di_node;	/* di_node in snapshot */
213*0Sstevel@tonic-gate 	dev_t		devt;		/* devt */
214*0Sstevel@tonic-gate 
215*0Sstevel@tonic-gate 	/*
216*0Sstevel@tonic-gate 	 * i_link ptr to links coming into this i_lnode node
217*0Sstevel@tonic-gate 	 * (this i_lnode is the target of these i_links)
218*0Sstevel@tonic-gate 	 */
219*0Sstevel@tonic-gate 	i_link_t	*link_in;
220*0Sstevel@tonic-gate 
221*0Sstevel@tonic-gate 	/*
222*0Sstevel@tonic-gate 	 * i_link ptr to links going out of this i_lnode node
223*0Sstevel@tonic-gate 	 * (this i_lnode is the source of these i_links)
224*0Sstevel@tonic-gate 	 */
225*0Sstevel@tonic-gate 	i_link_t	*link_out;
226*0Sstevel@tonic-gate } i_lnode_t;
227*0Sstevel@tonic-gate 
228*0Sstevel@tonic-gate /*
229*0Sstevel@tonic-gate  * Soft state associated with each instance of driver open.
230*0Sstevel@tonic-gate  */
231*0Sstevel@tonic-gate static struct di_state {
232*0Sstevel@tonic-gate 	di_off_t mem_size;	/* total # bytes in memlist	*/
233*0Sstevel@tonic-gate 	struct di_mem *memlist;	/* head of memlist		*/
234*0Sstevel@tonic-gate 	uint_t command;		/* command from ioctl		*/
235*0Sstevel@tonic-gate 	int di_iocstate;	/* snapshot ioctl state		*/
236*0Sstevel@tonic-gate 	mod_hash_t *reg_dip_hash;
237*0Sstevel@tonic-gate 	mod_hash_t *reg_pip_hash;
238*0Sstevel@tonic-gate 	int lnode_count;
239*0Sstevel@tonic-gate 	int link_count;
240*0Sstevel@tonic-gate 
241*0Sstevel@tonic-gate 	mod_hash_t *lnode_hash;
242*0Sstevel@tonic-gate 	mod_hash_t *link_hash;
243*0Sstevel@tonic-gate } **di_states;
244*0Sstevel@tonic-gate 
245*0Sstevel@tonic-gate static kmutex_t di_lock;	/* serialize instance assignment */
246*0Sstevel@tonic-gate 
247*0Sstevel@tonic-gate typedef enum {
248*0Sstevel@tonic-gate 	DI_QUIET = 0,	/* DI_QUIET must always be 0 */
249*0Sstevel@tonic-gate 	DI_ERR,
250*0Sstevel@tonic-gate 	DI_INFO,
251*0Sstevel@tonic-gate 	DI_TRACE,
252*0Sstevel@tonic-gate 	DI_TRACE1,
253*0Sstevel@tonic-gate 	DI_TRACE2
254*0Sstevel@tonic-gate } di_cache_debug_t;
255*0Sstevel@tonic-gate 
256*0Sstevel@tonic-gate static uint_t	di_chunk = 32;		/* I/O chunk size in pages */
257*0Sstevel@tonic-gate 
258*0Sstevel@tonic-gate #define	DI_CACHE_LOCK(c)	(mutex_enter(&(c).cache_lock))
259*0Sstevel@tonic-gate #define	DI_CACHE_UNLOCK(c)	(mutex_exit(&(c).cache_lock))
260*0Sstevel@tonic-gate #define	DI_CACHE_LOCKED(c)	(mutex_owned(&(c).cache_lock))
261*0Sstevel@tonic-gate 
262*0Sstevel@tonic-gate #define	CACHE_DEBUG(args)	\
263*0Sstevel@tonic-gate 	{ if (di_cache_debug != DI_QUIET) di_cache_print args; }
264*0Sstevel@tonic-gate 
265*0Sstevel@tonic-gate static int di_open(dev_t *, int, int, cred_t *);
266*0Sstevel@tonic-gate static int di_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
267*0Sstevel@tonic-gate static int di_close(dev_t, int, int, cred_t *);
268*0Sstevel@tonic-gate static int di_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
269*0Sstevel@tonic-gate static int di_attach(dev_info_t *, ddi_attach_cmd_t);
270*0Sstevel@tonic-gate static int di_detach(dev_info_t *, ddi_detach_cmd_t);
271*0Sstevel@tonic-gate 
272*0Sstevel@tonic-gate static di_off_t di_copyformat(di_off_t, struct di_state *, intptr_t, int);
273*0Sstevel@tonic-gate static di_off_t di_snapshot(struct di_state *);
274*0Sstevel@tonic-gate static di_off_t di_copydevnm(di_off_t *, struct di_state *);
275*0Sstevel@tonic-gate static di_off_t di_copytree(struct dev_info *, di_off_t *, struct di_state *);
276*0Sstevel@tonic-gate static di_off_t di_copynode(struct di_stack *, struct di_state *);
277*0Sstevel@tonic-gate static di_off_t di_getmdata(struct ddi_minor_data *, di_off_t *, di_off_t,
278*0Sstevel@tonic-gate     struct di_state *);
279*0Sstevel@tonic-gate static di_off_t di_getppdata(struct dev_info *, di_off_t *, struct di_state *);
280*0Sstevel@tonic-gate static di_off_t di_getdpdata(struct dev_info *, di_off_t *, struct di_state *);
281*0Sstevel@tonic-gate static di_off_t di_getprop(struct ddi_prop *, di_off_t *,
282*0Sstevel@tonic-gate     struct di_state *, struct dev_info *, int);
283*0Sstevel@tonic-gate static void di_allocmem(struct di_state *, size_t);
284*0Sstevel@tonic-gate static void di_freemem(struct di_state *);
285*0Sstevel@tonic-gate static void di_copymem(struct di_state *st, caddr_t buf, size_t bufsiz);
286*0Sstevel@tonic-gate static di_off_t di_checkmem(struct di_state *, di_off_t, size_t);
287*0Sstevel@tonic-gate static caddr_t di_mem_addr(struct di_state *, di_off_t);
288*0Sstevel@tonic-gate static int di_setstate(struct di_state *, int);
289*0Sstevel@tonic-gate static void di_register_dip(struct di_state *, dev_info_t *, di_off_t);
290*0Sstevel@tonic-gate static void di_register_pip(struct di_state *, mdi_pathinfo_t *, di_off_t);
291*0Sstevel@tonic-gate static di_off_t di_getpath_data(dev_info_t *, di_off_t *, di_off_t,
292*0Sstevel@tonic-gate     struct di_state *, int);
293*0Sstevel@tonic-gate static di_off_t di_getlink_data(di_off_t, struct di_state *);
294*0Sstevel@tonic-gate static int di_dip_find(struct di_state *st, dev_info_t *node, di_off_t *off_p);
295*0Sstevel@tonic-gate 
296*0Sstevel@tonic-gate static int cache_args_valid(struct di_state *st, int *error);
297*0Sstevel@tonic-gate static int snapshot_is_cacheable(struct di_state *st);
298*0Sstevel@tonic-gate static int di_cache_lookup(struct di_state *st);
299*0Sstevel@tonic-gate static int di_cache_update(struct di_state *st);
300*0Sstevel@tonic-gate static void di_cache_print(di_cache_debug_t msglevel, char *fmt, ...);
301*0Sstevel@tonic-gate 
302*0Sstevel@tonic-gate static struct cb_ops di_cb_ops = {
303*0Sstevel@tonic-gate 	di_open,		/* open */
304*0Sstevel@tonic-gate 	di_close,		/* close */
305*0Sstevel@tonic-gate 	nodev,			/* strategy */
306*0Sstevel@tonic-gate 	nodev,			/* print */
307*0Sstevel@tonic-gate 	nodev,			/* dump */
308*0Sstevel@tonic-gate 	nodev,			/* read */
309*0Sstevel@tonic-gate 	nodev,			/* write */
310*0Sstevel@tonic-gate 	di_ioctl,		/* ioctl */
311*0Sstevel@tonic-gate 	nodev,			/* devmap */
312*0Sstevel@tonic-gate 	nodev,			/* mmap */
313*0Sstevel@tonic-gate 	nodev,			/* segmap */
314*0Sstevel@tonic-gate 	nochpoll,		/* poll */
315*0Sstevel@tonic-gate 	ddi_prop_op,		/* prop_op */
316*0Sstevel@tonic-gate 	NULL,			/* streamtab  */
317*0Sstevel@tonic-gate 	D_NEW | D_MP		/* Driver compatibility flag */
318*0Sstevel@tonic-gate };
319*0Sstevel@tonic-gate 
320*0Sstevel@tonic-gate static struct dev_ops di_ops = {
321*0Sstevel@tonic-gate 	DEVO_REV,		/* devo_rev, */
322*0Sstevel@tonic-gate 	0,			/* refcnt  */
323*0Sstevel@tonic-gate 	di_info,		/* info */
324*0Sstevel@tonic-gate 	nulldev,		/* identify */
325*0Sstevel@tonic-gate 	nulldev,		/* probe */
326*0Sstevel@tonic-gate 	di_attach,		/* attach */
327*0Sstevel@tonic-gate 	di_detach,		/* detach */
328*0Sstevel@tonic-gate 	nodev,			/* reset */
329*0Sstevel@tonic-gate 	&di_cb_ops,		/* driver operations */
330*0Sstevel@tonic-gate 	NULL			/* bus operations */
331*0Sstevel@tonic-gate };
332*0Sstevel@tonic-gate 
333*0Sstevel@tonic-gate /*
334*0Sstevel@tonic-gate  * Module linkage information for the kernel.
335*0Sstevel@tonic-gate  */
336*0Sstevel@tonic-gate static struct modldrv modldrv = {
337*0Sstevel@tonic-gate 	&mod_driverops,
338*0Sstevel@tonic-gate 	"DEVINFO Driver %I%",
339*0Sstevel@tonic-gate 	&di_ops
340*0Sstevel@tonic-gate };
341*0Sstevel@tonic-gate 
342*0Sstevel@tonic-gate static struct modlinkage modlinkage = {
343*0Sstevel@tonic-gate 	MODREV_1,
344*0Sstevel@tonic-gate 	&modldrv,
345*0Sstevel@tonic-gate 	NULL
346*0Sstevel@tonic-gate };
347*0Sstevel@tonic-gate 
348*0Sstevel@tonic-gate int
349*0Sstevel@tonic-gate _init(void)
350*0Sstevel@tonic-gate {
351*0Sstevel@tonic-gate 	int	error;
352*0Sstevel@tonic-gate 
353*0Sstevel@tonic-gate 	mutex_init(&di_lock, NULL, MUTEX_DRIVER, NULL);
354*0Sstevel@tonic-gate 
355*0Sstevel@tonic-gate 	error = mod_install(&modlinkage);
356*0Sstevel@tonic-gate 	if (error != 0) {
357*0Sstevel@tonic-gate 		mutex_destroy(&di_lock);
358*0Sstevel@tonic-gate 		return (error);
359*0Sstevel@tonic-gate 	}
360*0Sstevel@tonic-gate 
361*0Sstevel@tonic-gate 	return (0);
362*0Sstevel@tonic-gate }
363*0Sstevel@tonic-gate 
364*0Sstevel@tonic-gate int
365*0Sstevel@tonic-gate _info(struct modinfo *modinfop)
366*0Sstevel@tonic-gate {
367*0Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
368*0Sstevel@tonic-gate }
369*0Sstevel@tonic-gate 
370*0Sstevel@tonic-gate int
371*0Sstevel@tonic-gate _fini(void)
372*0Sstevel@tonic-gate {
373*0Sstevel@tonic-gate 	int	error;
374*0Sstevel@tonic-gate 
375*0Sstevel@tonic-gate 	error = mod_remove(&modlinkage);
376*0Sstevel@tonic-gate 	if (error != 0) {
377*0Sstevel@tonic-gate 		return (error);
378*0Sstevel@tonic-gate 	}
379*0Sstevel@tonic-gate 
380*0Sstevel@tonic-gate 	mutex_destroy(&di_lock);
381*0Sstevel@tonic-gate 	return (0);
382*0Sstevel@tonic-gate }
383*0Sstevel@tonic-gate 
384*0Sstevel@tonic-gate static dev_info_t *di_dip;
385*0Sstevel@tonic-gate 
386*0Sstevel@tonic-gate /*ARGSUSED*/
387*0Sstevel@tonic-gate static int
388*0Sstevel@tonic-gate di_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
389*0Sstevel@tonic-gate {
390*0Sstevel@tonic-gate 	int error = DDI_FAILURE;
391*0Sstevel@tonic-gate 
392*0Sstevel@tonic-gate 	switch (infocmd) {
393*0Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
394*0Sstevel@tonic-gate 		*result = (void *)di_dip;
395*0Sstevel@tonic-gate 		error = DDI_SUCCESS;
396*0Sstevel@tonic-gate 		break;
397*0Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
398*0Sstevel@tonic-gate 		/*
399*0Sstevel@tonic-gate 		 * All dev_t's map to the same, single instance.
400*0Sstevel@tonic-gate 		 */
401*0Sstevel@tonic-gate 		*result = (void *)0;
402*0Sstevel@tonic-gate 		error = DDI_SUCCESS;
403*0Sstevel@tonic-gate 		break;
404*0Sstevel@tonic-gate 	default:
405*0Sstevel@tonic-gate 		break;
406*0Sstevel@tonic-gate 	}
407*0Sstevel@tonic-gate 
408*0Sstevel@tonic-gate 	return (error);
409*0Sstevel@tonic-gate }
410*0Sstevel@tonic-gate 
411*0Sstevel@tonic-gate static int
412*0Sstevel@tonic-gate di_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
413*0Sstevel@tonic-gate {
414*0Sstevel@tonic-gate 	int error = DDI_FAILURE;
415*0Sstevel@tonic-gate 
416*0Sstevel@tonic-gate 	switch (cmd) {
417*0Sstevel@tonic-gate 	case DDI_ATTACH:
418*0Sstevel@tonic-gate 		di_states = kmem_zalloc(
419*0Sstevel@tonic-gate 		    di_max_opens * sizeof (struct di_state *), KM_SLEEP);
420*0Sstevel@tonic-gate 
421*0Sstevel@tonic-gate 		if (ddi_create_minor_node(dip, "devinfo", S_IFCHR,
422*0Sstevel@tonic-gate 		    DI_FULL_PARENT, DDI_PSEUDO, NULL) == DDI_FAILURE ||
423*0Sstevel@tonic-gate 		    ddi_create_minor_node(dip, "devinfo,ro", S_IFCHR,
424*0Sstevel@tonic-gate 		    DI_READONLY_PARENT, DDI_PSEUDO, NULL) == DDI_FAILURE) {
425*0Sstevel@tonic-gate 			kmem_free(di_states,
426*0Sstevel@tonic-gate 			    di_max_opens * sizeof (struct di_state *));
427*0Sstevel@tonic-gate 			ddi_remove_minor_node(dip, NULL);
428*0Sstevel@tonic-gate 			error = DDI_FAILURE;
429*0Sstevel@tonic-gate 		} else {
430*0Sstevel@tonic-gate 			di_dip = dip;
431*0Sstevel@tonic-gate 			ddi_report_dev(dip);
432*0Sstevel@tonic-gate 
433*0Sstevel@tonic-gate 			error = DDI_SUCCESS;
434*0Sstevel@tonic-gate 		}
435*0Sstevel@tonic-gate 		break;
436*0Sstevel@tonic-gate 	default:
437*0Sstevel@tonic-gate 		error = DDI_FAILURE;
438*0Sstevel@tonic-gate 		break;
439*0Sstevel@tonic-gate 	}
440*0Sstevel@tonic-gate 
441*0Sstevel@tonic-gate 	return (error);
442*0Sstevel@tonic-gate }
443*0Sstevel@tonic-gate 
444*0Sstevel@tonic-gate static int
445*0Sstevel@tonic-gate di_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
446*0Sstevel@tonic-gate {
447*0Sstevel@tonic-gate 	int error = DDI_FAILURE;
448*0Sstevel@tonic-gate 
449*0Sstevel@tonic-gate 	switch (cmd) {
450*0Sstevel@tonic-gate 	case DDI_DETACH:
451*0Sstevel@tonic-gate 		ddi_remove_minor_node(dip, NULL);
452*0Sstevel@tonic-gate 		di_dip = NULL;
453*0Sstevel@tonic-gate 		kmem_free(di_states, di_max_opens * sizeof (struct di_state *));
454*0Sstevel@tonic-gate 
455*0Sstevel@tonic-gate 		error = DDI_SUCCESS;
456*0Sstevel@tonic-gate 		break;
457*0Sstevel@tonic-gate 	default:
458*0Sstevel@tonic-gate 		error = DDI_FAILURE;
459*0Sstevel@tonic-gate 		break;
460*0Sstevel@tonic-gate 	}
461*0Sstevel@tonic-gate 
462*0Sstevel@tonic-gate 	return (error);
463*0Sstevel@tonic-gate }
464*0Sstevel@tonic-gate 
465*0Sstevel@tonic-gate /*
466*0Sstevel@tonic-gate  * Allow multiple opens by tweaking the dev_t such that it looks like each
467*0Sstevel@tonic-gate  * open is getting a different minor device.  Each minor gets a separate
468*0Sstevel@tonic-gate  * entry in the di_states[] table.  Based on the original minor number, we
469*0Sstevel@tonic-gate  * discriminate opens of the full and read-only nodes.  If all of the instances
470*0Sstevel@tonic-gate  * of the selected minor node are currently open, we return EAGAIN.
471*0Sstevel@tonic-gate  */
472*0Sstevel@tonic-gate /*ARGSUSED*/
473*0Sstevel@tonic-gate static int
474*0Sstevel@tonic-gate di_open(dev_t *devp, int flag, int otyp, cred_t *credp)
475*0Sstevel@tonic-gate {
476*0Sstevel@tonic-gate 	int m;
477*0Sstevel@tonic-gate 	minor_t minor_parent = getminor(*devp);
478*0Sstevel@tonic-gate 
479*0Sstevel@tonic-gate 	if (minor_parent != DI_FULL_PARENT &&
480*0Sstevel@tonic-gate 	    minor_parent != DI_READONLY_PARENT)
481*0Sstevel@tonic-gate 		return (ENXIO);
482*0Sstevel@tonic-gate 
483*0Sstevel@tonic-gate 	mutex_enter(&di_lock);
484*0Sstevel@tonic-gate 
485*0Sstevel@tonic-gate 	for (m = minor_parent; m < di_max_opens; m += DI_NODE_SPECIES) {
486*0Sstevel@tonic-gate 		if (di_states[m] != NULL)
487*0Sstevel@tonic-gate 			continue;
488*0Sstevel@tonic-gate 
489*0Sstevel@tonic-gate 		di_states[m] = kmem_zalloc(sizeof (struct di_state), KM_SLEEP);
490*0Sstevel@tonic-gate 		break;	/* It's ours. */
491*0Sstevel@tonic-gate 	}
492*0Sstevel@tonic-gate 
493*0Sstevel@tonic-gate 	if (m >= di_max_opens) {
494*0Sstevel@tonic-gate 		/*
495*0Sstevel@tonic-gate 		 * maximum open instance for device reached
496*0Sstevel@tonic-gate 		 */
497*0Sstevel@tonic-gate 		mutex_exit(&di_lock);
498*0Sstevel@tonic-gate 		dcmn_err((CE_WARN, "devinfo: maximum devinfo open reached"));
499*0Sstevel@tonic-gate 		return (EAGAIN);
500*0Sstevel@tonic-gate 	}
501*0Sstevel@tonic-gate 	mutex_exit(&di_lock);
502*0Sstevel@tonic-gate 
503*0Sstevel@tonic-gate 	ASSERT(m < di_max_opens);
504*0Sstevel@tonic-gate 	*devp = makedevice(getmajor(*devp), (minor_t)(m + DI_NODE_SPECIES));
505*0Sstevel@tonic-gate 
506*0Sstevel@tonic-gate 	dcmn_err((CE_CONT, "di_open: thread = %p, assigned minor = %d\n",
507*0Sstevel@tonic-gate 		(void *)curthread, m + DI_NODE_SPECIES));
508*0Sstevel@tonic-gate 
509*0Sstevel@tonic-gate 	return (0);
510*0Sstevel@tonic-gate }
511*0Sstevel@tonic-gate 
512*0Sstevel@tonic-gate /*ARGSUSED*/
513*0Sstevel@tonic-gate static int
514*0Sstevel@tonic-gate di_close(dev_t dev, int flag, int otype, cred_t *cred_p)
515*0Sstevel@tonic-gate {
516*0Sstevel@tonic-gate 	struct di_state *st;
517*0Sstevel@tonic-gate 	int m = (int)getminor(dev) - DI_NODE_SPECIES;
518*0Sstevel@tonic-gate 
519*0Sstevel@tonic-gate 	if (m < 0) {
520*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "closing non-existent devinfo minor %d",
521*0Sstevel@tonic-gate 		    m + DI_NODE_SPECIES);
522*0Sstevel@tonic-gate 		return (ENXIO);
523*0Sstevel@tonic-gate 	}
524*0Sstevel@tonic-gate 
525*0Sstevel@tonic-gate 	st = di_states[m];
526*0Sstevel@tonic-gate 	ASSERT(m < di_max_opens && st != NULL);
527*0Sstevel@tonic-gate 
528*0Sstevel@tonic-gate 	di_freemem(st);
529*0Sstevel@tonic-gate 	kmem_free(st, sizeof (struct di_state));
530*0Sstevel@tonic-gate 
531*0Sstevel@tonic-gate 	/*
532*0Sstevel@tonic-gate 	 * empty slot in state table
533*0Sstevel@tonic-gate 	 */
534*0Sstevel@tonic-gate 	mutex_enter(&di_lock);
535*0Sstevel@tonic-gate 	di_states[m] = NULL;
536*0Sstevel@tonic-gate 	dcmn_err((CE_CONT, "di_close: thread = %p, assigned minor = %d\n",
537*0Sstevel@tonic-gate 		(void *)curthread, m + DI_NODE_SPECIES));
538*0Sstevel@tonic-gate 	mutex_exit(&di_lock);
539*0Sstevel@tonic-gate 
540*0Sstevel@tonic-gate 	return (0);
541*0Sstevel@tonic-gate }
542*0Sstevel@tonic-gate 
543*0Sstevel@tonic-gate 
544*0Sstevel@tonic-gate /*ARGSUSED*/
545*0Sstevel@tonic-gate static int
546*0Sstevel@tonic-gate di_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
547*0Sstevel@tonic-gate {
548*0Sstevel@tonic-gate 	int rv, error;
549*0Sstevel@tonic-gate 	di_off_t off;
550*0Sstevel@tonic-gate 	struct di_all *all;
551*0Sstevel@tonic-gate 	struct di_state *st;
552*0Sstevel@tonic-gate 	int m = (int)getminor(dev) - DI_NODE_SPECIES;
553*0Sstevel@tonic-gate 
554*0Sstevel@tonic-gate 	major_t i;
555*0Sstevel@tonic-gate 	char *drv_name;
556*0Sstevel@tonic-gate 	size_t map_size, size;
557*0Sstevel@tonic-gate 	struct di_mem *dcp;
558*0Sstevel@tonic-gate 	int ndi_flags;
559*0Sstevel@tonic-gate 
560*0Sstevel@tonic-gate 	if (m < 0 || m >= di_max_opens) {
561*0Sstevel@tonic-gate 		return (ENXIO);
562*0Sstevel@tonic-gate 	}
563*0Sstevel@tonic-gate 
564*0Sstevel@tonic-gate 	st = di_states[m];
565*0Sstevel@tonic-gate 	ASSERT(st != NULL);
566*0Sstevel@tonic-gate 
567*0Sstevel@tonic-gate 	dcmn_err2((CE_CONT, "di_ioctl: mode = %x, cmd = %x\n", mode, cmd));
568*0Sstevel@tonic-gate 
569*0Sstevel@tonic-gate 	switch (cmd) {
570*0Sstevel@tonic-gate 	case DINFOIDENT:
571*0Sstevel@tonic-gate 		/*
572*0Sstevel@tonic-gate 		 * This is called from di_init to verify that the driver
573*0Sstevel@tonic-gate 		 * opened is indeed devinfo. The purpose is to guard against
574*0Sstevel@tonic-gate 		 * sending ioctl to an unknown driver in case of an
575*0Sstevel@tonic-gate 		 * unresolved major number conflict during bfu.
576*0Sstevel@tonic-gate 		 */
577*0Sstevel@tonic-gate 		*rvalp = DI_MAGIC;
578*0Sstevel@tonic-gate 		return (0);
579*0Sstevel@tonic-gate 
580*0Sstevel@tonic-gate 	case DINFOLODRV:
581*0Sstevel@tonic-gate 		/*
582*0Sstevel@tonic-gate 		 * Hold an installed driver and return the result
583*0Sstevel@tonic-gate 		 */
584*0Sstevel@tonic-gate 		if (DI_UNPRIVILEGED_NODE(m)) {
585*0Sstevel@tonic-gate 			/*
586*0Sstevel@tonic-gate 			 * Only the fully enabled instances may issue
587*0Sstevel@tonic-gate 			 * DINFOLDDRV.
588*0Sstevel@tonic-gate 			 */
589*0Sstevel@tonic-gate 			return (EACCES);
590*0Sstevel@tonic-gate 		}
591*0Sstevel@tonic-gate 
592*0Sstevel@tonic-gate 		drv_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
593*0Sstevel@tonic-gate 		if (ddi_copyin((void *)arg, drv_name, MAXNAMELEN, mode) != 0) {
594*0Sstevel@tonic-gate 			kmem_free(drv_name, MAXNAMELEN);
595*0Sstevel@tonic-gate 			return (EFAULT);
596*0Sstevel@tonic-gate 		}
597*0Sstevel@tonic-gate 
598*0Sstevel@tonic-gate 		/*
599*0Sstevel@tonic-gate 		 * Some 3rd party driver's _init() walks the device tree,
600*0Sstevel@tonic-gate 		 * so we load the driver module before configuring driver.
601*0Sstevel@tonic-gate 		 */
602*0Sstevel@tonic-gate 		i = ddi_name_to_major(drv_name);
603*0Sstevel@tonic-gate 		if (ddi_hold_driver(i) == NULL) {
604*0Sstevel@tonic-gate 			kmem_free(drv_name, MAXNAMELEN);
605*0Sstevel@tonic-gate 			return (ENXIO);
606*0Sstevel@tonic-gate 		}
607*0Sstevel@tonic-gate 
608*0Sstevel@tonic-gate 		ndi_flags = NDI_DEVI_PERSIST | NDI_CONFIG | NDI_NO_EVENT;
609*0Sstevel@tonic-gate 
610*0Sstevel@tonic-gate 		/*
611*0Sstevel@tonic-gate 		 * i_ddi_load_drvconf() below will trigger a reprobe
612*0Sstevel@tonic-gate 		 * via reset_nexus_flags(). NDI_DRV_CONF_REPROBE isn't
613*0Sstevel@tonic-gate 		 * needed here.
614*0Sstevel@tonic-gate 		 */
615*0Sstevel@tonic-gate 		modunload_disable();
616*0Sstevel@tonic-gate 		(void) i_ddi_load_drvconf(i);
617*0Sstevel@tonic-gate 		(void) ndi_devi_config_driver(ddi_root_node(), ndi_flags, i);
618*0Sstevel@tonic-gate 		kmem_free(drv_name, MAXNAMELEN);
619*0Sstevel@tonic-gate 		ddi_rele_driver(i);
620*0Sstevel@tonic-gate 		rv = i_ddi_devs_attached(i);
621*0Sstevel@tonic-gate 		modunload_enable();
622*0Sstevel@tonic-gate 
623*0Sstevel@tonic-gate 		i_ddi_di_cache_invalidate(KM_SLEEP);
624*0Sstevel@tonic-gate 
625*0Sstevel@tonic-gate 		return ((rv == DDI_SUCCESS)? 0 : ENXIO);
626*0Sstevel@tonic-gate 
627*0Sstevel@tonic-gate 	case DINFOUSRLD:
628*0Sstevel@tonic-gate 		/*
629*0Sstevel@tonic-gate 		 * The case for copying snapshot to userland
630*0Sstevel@tonic-gate 		 */
631*0Sstevel@tonic-gate 		if (di_setstate(st, IOC_COPY) == -1)
632*0Sstevel@tonic-gate 			return (EBUSY);
633*0Sstevel@tonic-gate 
634*0Sstevel@tonic-gate 		map_size = ((struct di_all *)di_mem_addr(st, 0))->map_size;
635*0Sstevel@tonic-gate 		if (map_size == 0) {
636*0Sstevel@tonic-gate 			(void) di_setstate(st, IOC_DONE);
637*0Sstevel@tonic-gate 			return (EFAULT);
638*0Sstevel@tonic-gate 		}
639*0Sstevel@tonic-gate 
640*0Sstevel@tonic-gate 		/*
641*0Sstevel@tonic-gate 		 * copyout the snapshot
642*0Sstevel@tonic-gate 		 */
643*0Sstevel@tonic-gate 		map_size = (map_size + PAGEOFFSET) & PAGEMASK;
644*0Sstevel@tonic-gate 
645*0Sstevel@tonic-gate 		/*
646*0Sstevel@tonic-gate 		 * Return the map size, so caller may do a sanity
647*0Sstevel@tonic-gate 		 * check against the return value of snapshot ioctl()
648*0Sstevel@tonic-gate 		 */
649*0Sstevel@tonic-gate 		*rvalp = (int)map_size;
650*0Sstevel@tonic-gate 
651*0Sstevel@tonic-gate 		/*
652*0Sstevel@tonic-gate 		 * Copy one chunk at a time
653*0Sstevel@tonic-gate 		 */
654*0Sstevel@tonic-gate 		off = 0;
655*0Sstevel@tonic-gate 		dcp = st->memlist;
656*0Sstevel@tonic-gate 		while (map_size) {
657*0Sstevel@tonic-gate 			size = dcp->buf_size;
658*0Sstevel@tonic-gate 			if (map_size <= size) {
659*0Sstevel@tonic-gate 				size = map_size;
660*0Sstevel@tonic-gate 			}
661*0Sstevel@tonic-gate 
662*0Sstevel@tonic-gate 			if (ddi_copyout(di_mem_addr(st, off),
663*0Sstevel@tonic-gate 			    (void *)(arg + off), size, mode) != 0) {
664*0Sstevel@tonic-gate 				(void) di_setstate(st, IOC_DONE);
665*0Sstevel@tonic-gate 				return (EFAULT);
666*0Sstevel@tonic-gate 			}
667*0Sstevel@tonic-gate 
668*0Sstevel@tonic-gate 			map_size -= size;
669*0Sstevel@tonic-gate 			off += size;
670*0Sstevel@tonic-gate 			dcp = dcp->next;
671*0Sstevel@tonic-gate 		}
672*0Sstevel@tonic-gate 
673*0Sstevel@tonic-gate 		di_freemem(st);
674*0Sstevel@tonic-gate 		(void) di_setstate(st, IOC_IDLE);
675*0Sstevel@tonic-gate 		return (0);
676*0Sstevel@tonic-gate 
677*0Sstevel@tonic-gate 	default:
678*0Sstevel@tonic-gate 		if ((cmd & ~DIIOC_MASK) != DIIOC) {
679*0Sstevel@tonic-gate 			/*
680*0Sstevel@tonic-gate 			 * Invalid ioctl command
681*0Sstevel@tonic-gate 			 */
682*0Sstevel@tonic-gate 			return (ENOTTY);
683*0Sstevel@tonic-gate 		}
684*0Sstevel@tonic-gate 		/*
685*0Sstevel@tonic-gate 		 * take a snapshot
686*0Sstevel@tonic-gate 		 */
687*0Sstevel@tonic-gate 		st->command = cmd & DIIOC_MASK;
688*0Sstevel@tonic-gate 		/*FALLTHROUGH*/
689*0Sstevel@tonic-gate 	}
690*0Sstevel@tonic-gate 
691*0Sstevel@tonic-gate 	/*
692*0Sstevel@tonic-gate 	 * Obtain enough memory to hold header + rootpath.  We prevent kernel
693*0Sstevel@tonic-gate 	 * memory exhaustion by freeing any previously allocated snapshot and
694*0Sstevel@tonic-gate 	 * refusing the operation; otherwise we would be allowing ioctl(),
695*0Sstevel@tonic-gate 	 * ioctl(), ioctl(), ..., panic.
696*0Sstevel@tonic-gate 	 */
697*0Sstevel@tonic-gate 	if (di_setstate(st, IOC_SNAP) == -1)
698*0Sstevel@tonic-gate 		return (EBUSY);
699*0Sstevel@tonic-gate 
700*0Sstevel@tonic-gate 	size = sizeof (struct di_all) +
701*0Sstevel@tonic-gate 	    sizeof (((struct dinfo_io *)(NULL))->root_path);
702*0Sstevel@tonic-gate 	if (size < PAGESIZE)
703*0Sstevel@tonic-gate 		size = PAGESIZE;
704*0Sstevel@tonic-gate 	di_allocmem(st, size);
705*0Sstevel@tonic-gate 
706*0Sstevel@tonic-gate 	all = (struct di_all *)di_mem_addr(st, 0);
707*0Sstevel@tonic-gate 	all->devcnt = devcnt;
708*0Sstevel@tonic-gate 	all->command = st->command;
709*0Sstevel@tonic-gate 	all->version = DI_SNAPSHOT_VERSION;
710*0Sstevel@tonic-gate 
711*0Sstevel@tonic-gate 	/*
712*0Sstevel@tonic-gate 	 * Note the endianness in case we need to transport snapshot
713*0Sstevel@tonic-gate 	 * over the network.
714*0Sstevel@tonic-gate 	 */
715*0Sstevel@tonic-gate #if defined(_LITTLE_ENDIAN)
716*0Sstevel@tonic-gate 	all->endianness = DI_LITTLE_ENDIAN;
717*0Sstevel@tonic-gate #else
718*0Sstevel@tonic-gate 	all->endianness = DI_BIG_ENDIAN;
719*0Sstevel@tonic-gate #endif
720*0Sstevel@tonic-gate 
721*0Sstevel@tonic-gate 	/* Copyin ioctl args, store in the snapshot. */
722*0Sstevel@tonic-gate 	if (copyinstr((void *)arg, all->root_path,
723*0Sstevel@tonic-gate 	    sizeof (((struct dinfo_io *)(NULL))->root_path), &size) != 0) {
724*0Sstevel@tonic-gate 		di_freemem(st);
725*0Sstevel@tonic-gate 		(void) di_setstate(st, IOC_IDLE);
726*0Sstevel@tonic-gate 		return (EFAULT);
727*0Sstevel@tonic-gate 	}
728*0Sstevel@tonic-gate 
729*0Sstevel@tonic-gate 	error = 0;
730*0Sstevel@tonic-gate 	if ((st->command & DINFOCACHE) && !cache_args_valid(st, &error)) {
731*0Sstevel@tonic-gate 		di_freemem(st);
732*0Sstevel@tonic-gate 		(void) di_setstate(st, IOC_IDLE);
733*0Sstevel@tonic-gate 		return (error);
734*0Sstevel@tonic-gate 	}
735*0Sstevel@tonic-gate 
736*0Sstevel@tonic-gate 	off = DI_ALIGN(sizeof (struct di_all) + size);
737*0Sstevel@tonic-gate 
738*0Sstevel@tonic-gate 	/*
739*0Sstevel@tonic-gate 	 * Only the fully enabled version may force load drivers or read
740*0Sstevel@tonic-gate 	 * the parent private data from a driver.
741*0Sstevel@tonic-gate 	 */
742*0Sstevel@tonic-gate 	if ((st->command & (DINFOPRIVDATA | DINFOFORCE)) != 0 &&
743*0Sstevel@tonic-gate 	    DI_UNPRIVILEGED_NODE(m)) {
744*0Sstevel@tonic-gate 		di_freemem(st);
745*0Sstevel@tonic-gate 		(void) di_setstate(st, IOC_IDLE);
746*0Sstevel@tonic-gate 		return (EACCES);
747*0Sstevel@tonic-gate 	}
748*0Sstevel@tonic-gate 
749*0Sstevel@tonic-gate 	/* Do we need private data? */
750*0Sstevel@tonic-gate 	if (st->command & DINFOPRIVDATA) {
751*0Sstevel@tonic-gate 		arg += sizeof (((struct dinfo_io *)(NULL))->root_path);
752*0Sstevel@tonic-gate 
753*0Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
754*0Sstevel@tonic-gate 		switch (ddi_model_convert_from(mode & FMODELS)) {
755*0Sstevel@tonic-gate 		case DDI_MODEL_ILP32: {
756*0Sstevel@tonic-gate 			/*
757*0Sstevel@tonic-gate 			 * Cannot copy private data from 64-bit kernel
758*0Sstevel@tonic-gate 			 * to 32-bit app
759*0Sstevel@tonic-gate 			 */
760*0Sstevel@tonic-gate 			di_freemem(st);
761*0Sstevel@tonic-gate 			(void) di_setstate(st, IOC_IDLE);
762*0Sstevel@tonic-gate 			return (EINVAL);
763*0Sstevel@tonic-gate 		}
764*0Sstevel@tonic-gate 		case DDI_MODEL_NONE:
765*0Sstevel@tonic-gate 			if ((off = di_copyformat(off, st, arg, mode)) == 0) {
766*0Sstevel@tonic-gate 				di_freemem(st);
767*0Sstevel@tonic-gate 				(void) di_setstate(st, IOC_IDLE);
768*0Sstevel@tonic-gate 				return (EFAULT);
769*0Sstevel@tonic-gate 			}
770*0Sstevel@tonic-gate 			break;
771*0Sstevel@tonic-gate 		}
772*0Sstevel@tonic-gate #else /* !_MULTI_DATAMODEL */
773*0Sstevel@tonic-gate 		if ((off = di_copyformat(off, st, arg, mode)) == 0) {
774*0Sstevel@tonic-gate 			di_freemem(st);
775*0Sstevel@tonic-gate 			(void) di_setstate(st, IOC_IDLE);
776*0Sstevel@tonic-gate 			return (EFAULT);
777*0Sstevel@tonic-gate 		}
778*0Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
779*0Sstevel@tonic-gate 	}
780*0Sstevel@tonic-gate 
781*0Sstevel@tonic-gate 	all->top_devinfo = DI_ALIGN(off);
782*0Sstevel@tonic-gate 
783*0Sstevel@tonic-gate 	/*
784*0Sstevel@tonic-gate 	 * For cache lookups we reallocate memory from scratch,
785*0Sstevel@tonic-gate 	 * so the value of "all" is no longer valid.
786*0Sstevel@tonic-gate 	 */
787*0Sstevel@tonic-gate 	all = NULL;
788*0Sstevel@tonic-gate 
789*0Sstevel@tonic-gate 	if (st->command & DINFOCACHE) {
790*0Sstevel@tonic-gate 		*rvalp = di_cache_lookup(st);
791*0Sstevel@tonic-gate 	} else if (snapshot_is_cacheable(st)) {
792*0Sstevel@tonic-gate 		DI_CACHE_LOCK(di_cache);
793*0Sstevel@tonic-gate 		*rvalp = di_cache_update(st);
794*0Sstevel@tonic-gate 		DI_CACHE_UNLOCK(di_cache);
795*0Sstevel@tonic-gate 	} else {
796*0Sstevel@tonic-gate 		modunload_disable();
797*0Sstevel@tonic-gate 		*rvalp = di_snapshot(st);
798*0Sstevel@tonic-gate 		modunload_enable();
799*0Sstevel@tonic-gate 	}
800*0Sstevel@tonic-gate 
801*0Sstevel@tonic-gate 	if (*rvalp) {
802*0Sstevel@tonic-gate 		DI_ALL_PTR(st)->map_size = *rvalp;
803*0Sstevel@tonic-gate 		(void) di_setstate(st, IOC_DONE);
804*0Sstevel@tonic-gate 	} else {
805*0Sstevel@tonic-gate 		di_freemem(st);
806*0Sstevel@tonic-gate 		(void) di_setstate(st, IOC_IDLE);
807*0Sstevel@tonic-gate 	}
808*0Sstevel@tonic-gate 
809*0Sstevel@tonic-gate 	return (0);
810*0Sstevel@tonic-gate }
811*0Sstevel@tonic-gate 
812*0Sstevel@tonic-gate /*
813*0Sstevel@tonic-gate  * Get a chunk of memory >= size, for the snapshot
814*0Sstevel@tonic-gate  */
815*0Sstevel@tonic-gate static void
816*0Sstevel@tonic-gate di_allocmem(struct di_state *st, size_t size)
817*0Sstevel@tonic-gate {
818*0Sstevel@tonic-gate 	struct di_mem *mem = kmem_zalloc(sizeof (struct di_mem),
819*0Sstevel@tonic-gate 	    KM_SLEEP);
820*0Sstevel@tonic-gate 	/*
821*0Sstevel@tonic-gate 	 * Round up size to nearest power of 2. If it is less
822*0Sstevel@tonic-gate 	 * than st->mem_size, set it to st->mem_size (i.e.,
823*0Sstevel@tonic-gate 	 * the mem_size is doubled every time) to reduce the
824*0Sstevel@tonic-gate 	 * number of memory allocations.
825*0Sstevel@tonic-gate 	 */
826*0Sstevel@tonic-gate 	size_t tmp = 1;
827*0Sstevel@tonic-gate 	while (tmp < size) {
828*0Sstevel@tonic-gate 		tmp <<= 1;
829*0Sstevel@tonic-gate 	}
830*0Sstevel@tonic-gate 	size = (tmp > st->mem_size) ? tmp : st->mem_size;
831*0Sstevel@tonic-gate 
832*0Sstevel@tonic-gate 	mem->buf = ddi_umem_alloc(size, DDI_UMEM_SLEEP, &mem->cook);
833*0Sstevel@tonic-gate 	mem->buf_size = size;
834*0Sstevel@tonic-gate 
835*0Sstevel@tonic-gate 	dcmn_err2((CE_CONT, "di_allocmem: mem_size=%x\n", st->mem_size));
836*0Sstevel@tonic-gate 
837*0Sstevel@tonic-gate 	if (st->mem_size == 0) {	/* first chunk */
838*0Sstevel@tonic-gate 		st->memlist = mem;
839*0Sstevel@tonic-gate 	} else {
840*0Sstevel@tonic-gate 		/*
841*0Sstevel@tonic-gate 		 * locate end of linked list and add a chunk at the end
842*0Sstevel@tonic-gate 		 */
843*0Sstevel@tonic-gate 		struct di_mem *dcp = st->memlist;
844*0Sstevel@tonic-gate 		while (dcp->next != NULL) {
845*0Sstevel@tonic-gate 			dcp = dcp->next;
846*0Sstevel@tonic-gate 		}
847*0Sstevel@tonic-gate 
848*0Sstevel@tonic-gate 		dcp->next = mem;
849*0Sstevel@tonic-gate 	}
850*0Sstevel@tonic-gate 
851*0Sstevel@tonic-gate 	st->mem_size += size;
852*0Sstevel@tonic-gate }
853*0Sstevel@tonic-gate 
854*0Sstevel@tonic-gate /*
855*0Sstevel@tonic-gate  * Copy upto bufsiz bytes of the memlist to buf
856*0Sstevel@tonic-gate  */
857*0Sstevel@tonic-gate static void
858*0Sstevel@tonic-gate di_copymem(struct di_state *st, caddr_t buf, size_t bufsiz)
859*0Sstevel@tonic-gate {
860*0Sstevel@tonic-gate 	struct di_mem *dcp;
861*0Sstevel@tonic-gate 	size_t copysz;
862*0Sstevel@tonic-gate 
863*0Sstevel@tonic-gate 	if (st->mem_size == 0) {
864*0Sstevel@tonic-gate 		ASSERT(st->memlist == NULL);
865*0Sstevel@tonic-gate 		return;
866*0Sstevel@tonic-gate 	}
867*0Sstevel@tonic-gate 
868*0Sstevel@tonic-gate 	copysz = 0;
869*0Sstevel@tonic-gate 	for (dcp = st->memlist; dcp; dcp = dcp->next) {
870*0Sstevel@tonic-gate 
871*0Sstevel@tonic-gate 		ASSERT(bufsiz > 0);
872*0Sstevel@tonic-gate 
873*0Sstevel@tonic-gate 		if (bufsiz <= dcp->buf_size)
874*0Sstevel@tonic-gate 			copysz = bufsiz;
875*0Sstevel@tonic-gate 		else
876*0Sstevel@tonic-gate 			copysz = dcp->buf_size;
877*0Sstevel@tonic-gate 
878*0Sstevel@tonic-gate 		bcopy(dcp->buf, buf, copysz);
879*0Sstevel@tonic-gate 
880*0Sstevel@tonic-gate 		buf += copysz;
881*0Sstevel@tonic-gate 		bufsiz -= copysz;
882*0Sstevel@tonic-gate 
883*0Sstevel@tonic-gate 		if (bufsiz == 0)
884*0Sstevel@tonic-gate 			break;
885*0Sstevel@tonic-gate 	}
886*0Sstevel@tonic-gate }
887*0Sstevel@tonic-gate 
888*0Sstevel@tonic-gate /*
889*0Sstevel@tonic-gate  * Free all memory for the snapshot
890*0Sstevel@tonic-gate  */
891*0Sstevel@tonic-gate static void
892*0Sstevel@tonic-gate di_freemem(struct di_state *st)
893*0Sstevel@tonic-gate {
894*0Sstevel@tonic-gate 	struct di_mem *dcp, *tmp;
895*0Sstevel@tonic-gate 
896*0Sstevel@tonic-gate 	dcmn_err2((CE_CONT, "di_freemem\n"));
897*0Sstevel@tonic-gate 
898*0Sstevel@tonic-gate 	if (st->mem_size) {
899*0Sstevel@tonic-gate 		dcp = st->memlist;
900*0Sstevel@tonic-gate 		while (dcp) {	/* traverse the linked list */
901*0Sstevel@tonic-gate 			tmp = dcp;
902*0Sstevel@tonic-gate 			dcp = dcp->next;
903*0Sstevel@tonic-gate 			ddi_umem_free(tmp->cook);
904*0Sstevel@tonic-gate 			kmem_free(tmp, sizeof (struct di_mem));
905*0Sstevel@tonic-gate 		}
906*0Sstevel@tonic-gate 		st->mem_size = 0;
907*0Sstevel@tonic-gate 		st->memlist = NULL;
908*0Sstevel@tonic-gate 	}
909*0Sstevel@tonic-gate 
910*0Sstevel@tonic-gate 	ASSERT(st->mem_size == 0);
911*0Sstevel@tonic-gate 	ASSERT(st->memlist == NULL);
912*0Sstevel@tonic-gate }
913*0Sstevel@tonic-gate 
914*0Sstevel@tonic-gate /*
915*0Sstevel@tonic-gate  * Copies cached data to the di_state structure.
916*0Sstevel@tonic-gate  * Returns:
917*0Sstevel@tonic-gate  *	- size of data copied, on SUCCESS
918*0Sstevel@tonic-gate  *	- 0 on failure
919*0Sstevel@tonic-gate  */
920*0Sstevel@tonic-gate static int
921*0Sstevel@tonic-gate di_cache2mem(struct di_cache *cache, struct di_state *st)
922*0Sstevel@tonic-gate {
923*0Sstevel@tonic-gate 	caddr_t	pa;
924*0Sstevel@tonic-gate 
925*0Sstevel@tonic-gate 	ASSERT(st->mem_size == 0);
926*0Sstevel@tonic-gate 	ASSERT(st->memlist == NULL);
927*0Sstevel@tonic-gate 	ASSERT(!servicing_interrupt());
928*0Sstevel@tonic-gate 	ASSERT(DI_CACHE_LOCKED(*cache));
929*0Sstevel@tonic-gate 
930*0Sstevel@tonic-gate 	if (cache->cache_size == 0) {
931*0Sstevel@tonic-gate 		ASSERT(cache->cache_data == NULL);
932*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "Empty cache. Skipping copy"));
933*0Sstevel@tonic-gate 		return (0);
934*0Sstevel@tonic-gate 	}
935*0Sstevel@tonic-gate 
936*0Sstevel@tonic-gate 	ASSERT(cache->cache_data);
937*0Sstevel@tonic-gate 
938*0Sstevel@tonic-gate 	di_allocmem(st, cache->cache_size);
939*0Sstevel@tonic-gate 
940*0Sstevel@tonic-gate 	pa = di_mem_addr(st, 0);
941*0Sstevel@tonic-gate 
942*0Sstevel@tonic-gate 	ASSERT(pa);
943*0Sstevel@tonic-gate 
944*0Sstevel@tonic-gate 	/*
945*0Sstevel@tonic-gate 	 * Verify that di_allocmem() allocates contiguous memory,
946*0Sstevel@tonic-gate 	 * so that it is safe to do straight bcopy()
947*0Sstevel@tonic-gate 	 */
948*0Sstevel@tonic-gate 	ASSERT(st->memlist != NULL);
949*0Sstevel@tonic-gate 	ASSERT(st->memlist->next == NULL);
950*0Sstevel@tonic-gate 	bcopy(cache->cache_data, pa, cache->cache_size);
951*0Sstevel@tonic-gate 
952*0Sstevel@tonic-gate 	return (cache->cache_size);
953*0Sstevel@tonic-gate }
954*0Sstevel@tonic-gate 
955*0Sstevel@tonic-gate /*
956*0Sstevel@tonic-gate  * Copies a snapshot from di_state to the cache
957*0Sstevel@tonic-gate  * Returns:
958*0Sstevel@tonic-gate  *	- 0 on failure
959*0Sstevel@tonic-gate  *	- size of copied data on success
960*0Sstevel@tonic-gate  */
961*0Sstevel@tonic-gate static int
962*0Sstevel@tonic-gate di_mem2cache(struct di_state *st, struct di_cache *cache)
963*0Sstevel@tonic-gate {
964*0Sstevel@tonic-gate 	size_t map_size;
965*0Sstevel@tonic-gate 
966*0Sstevel@tonic-gate 	ASSERT(cache->cache_size == 0);
967*0Sstevel@tonic-gate 	ASSERT(cache->cache_data == NULL);
968*0Sstevel@tonic-gate 	ASSERT(!servicing_interrupt());
969*0Sstevel@tonic-gate 	ASSERT(DI_CACHE_LOCKED(*cache));
970*0Sstevel@tonic-gate 
971*0Sstevel@tonic-gate 	if (st->mem_size == 0) {
972*0Sstevel@tonic-gate 		ASSERT(st->memlist == NULL);
973*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "Empty memlist. Skipping copy"));
974*0Sstevel@tonic-gate 		return (0);
975*0Sstevel@tonic-gate 	}
976*0Sstevel@tonic-gate 
977*0Sstevel@tonic-gate 	ASSERT(st->memlist);
978*0Sstevel@tonic-gate 
979*0Sstevel@tonic-gate 	/*
980*0Sstevel@tonic-gate 	 * The size of the memory list may be much larger than the
981*0Sstevel@tonic-gate 	 * size of valid data (map_size). Cache only the valid data
982*0Sstevel@tonic-gate 	 */
983*0Sstevel@tonic-gate 	map_size = DI_ALL_PTR(st)->map_size;
984*0Sstevel@tonic-gate 	if (map_size == 0 || map_size < sizeof (struct di_all) ||
985*0Sstevel@tonic-gate 	    map_size > st->mem_size) {
986*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "cannot cache: bad size: 0x%x", map_size));
987*0Sstevel@tonic-gate 		return (0);
988*0Sstevel@tonic-gate 	}
989*0Sstevel@tonic-gate 
990*0Sstevel@tonic-gate 	cache->cache_data = kmem_alloc(map_size, KM_SLEEP);
991*0Sstevel@tonic-gate 	cache->cache_size = map_size;
992*0Sstevel@tonic-gate 	di_copymem(st, cache->cache_data, cache->cache_size);
993*0Sstevel@tonic-gate 
994*0Sstevel@tonic-gate 	return (map_size);
995*0Sstevel@tonic-gate }
996*0Sstevel@tonic-gate 
997*0Sstevel@tonic-gate /*
998*0Sstevel@tonic-gate  * Make sure there is at least "size" bytes memory left before
999*0Sstevel@tonic-gate  * going on. Otherwise, start on a new chunk.
1000*0Sstevel@tonic-gate  */
1001*0Sstevel@tonic-gate static di_off_t
1002*0Sstevel@tonic-gate di_checkmem(struct di_state *st, di_off_t off, size_t size)
1003*0Sstevel@tonic-gate {
1004*0Sstevel@tonic-gate 	dcmn_err3((CE_CONT, "di_checkmem: off=%x size=%x\n",
1005*0Sstevel@tonic-gate 			off, (int)size));
1006*0Sstevel@tonic-gate 
1007*0Sstevel@tonic-gate 	/*
1008*0Sstevel@tonic-gate 	 * di_checkmem() shouldn't be called with a size of zero.
1009*0Sstevel@tonic-gate 	 * But in case it is, we want to make sure we return a valid
1010*0Sstevel@tonic-gate 	 * offset within the memlist and not an offset that points us
1011*0Sstevel@tonic-gate 	 * at the end of the memlist.
1012*0Sstevel@tonic-gate 	 */
1013*0Sstevel@tonic-gate 	if (size == 0) {
1014*0Sstevel@tonic-gate 		dcmn_err((CE_WARN, "di_checkmem: invalid zero size used"));
1015*0Sstevel@tonic-gate 		size = 1;
1016*0Sstevel@tonic-gate 	}
1017*0Sstevel@tonic-gate 
1018*0Sstevel@tonic-gate 	off = DI_ALIGN(off);
1019*0Sstevel@tonic-gate 	if ((st->mem_size - off) < size) {
1020*0Sstevel@tonic-gate 		off = st->mem_size;
1021*0Sstevel@tonic-gate 		di_allocmem(st, size);
1022*0Sstevel@tonic-gate 	}
1023*0Sstevel@tonic-gate 
1024*0Sstevel@tonic-gate 	return (off);
1025*0Sstevel@tonic-gate }
1026*0Sstevel@tonic-gate 
1027*0Sstevel@tonic-gate /*
1028*0Sstevel@tonic-gate  * Copy the private data format from ioctl arg.
1029*0Sstevel@tonic-gate  * On success, the ending offset is returned. On error 0 is returned.
1030*0Sstevel@tonic-gate  */
1031*0Sstevel@tonic-gate static di_off_t
1032*0Sstevel@tonic-gate di_copyformat(di_off_t off, struct di_state *st, intptr_t arg, int mode)
1033*0Sstevel@tonic-gate {
1034*0Sstevel@tonic-gate 	di_off_t size;
1035*0Sstevel@tonic-gate 	struct di_priv_data *priv;
1036*0Sstevel@tonic-gate 	struct di_all *all = (struct di_all *)di_mem_addr(st, 0);
1037*0Sstevel@tonic-gate 
1038*0Sstevel@tonic-gate 	dcmn_err2((CE_CONT, "di_copyformat: off=%x, arg=%p mode=%x\n",
1039*0Sstevel@tonic-gate 		off, (void *)arg, mode));
1040*0Sstevel@tonic-gate 
1041*0Sstevel@tonic-gate 	/*
1042*0Sstevel@tonic-gate 	 * Copyin data and check version.
1043*0Sstevel@tonic-gate 	 * We only handle private data version 0.
1044*0Sstevel@tonic-gate 	 */
1045*0Sstevel@tonic-gate 	priv = kmem_alloc(sizeof (struct di_priv_data), KM_SLEEP);
1046*0Sstevel@tonic-gate 	if ((ddi_copyin((void *)arg, priv, sizeof (struct di_priv_data),
1047*0Sstevel@tonic-gate 	    mode) != 0) || (priv->version != DI_PRIVDATA_VERSION_0)) {
1048*0Sstevel@tonic-gate 		kmem_free(priv, sizeof (struct di_priv_data));
1049*0Sstevel@tonic-gate 		return (0);
1050*0Sstevel@tonic-gate 	}
1051*0Sstevel@tonic-gate 
1052*0Sstevel@tonic-gate 	/*
1053*0Sstevel@tonic-gate 	 * Save di_priv_data copied from userland in snapshot.
1054*0Sstevel@tonic-gate 	 */
1055*0Sstevel@tonic-gate 	all->pd_version = priv->version;
1056*0Sstevel@tonic-gate 	all->n_ppdata = priv->n_parent;
1057*0Sstevel@tonic-gate 	all->n_dpdata = priv->n_driver;
1058*0Sstevel@tonic-gate 
1059*0Sstevel@tonic-gate 	/*
1060*0Sstevel@tonic-gate 	 * copyin private data format, modify offset accordingly
1061*0Sstevel@tonic-gate 	 */
1062*0Sstevel@tonic-gate 	if (all->n_ppdata) {	/* parent private data format */
1063*0Sstevel@tonic-gate 		/*
1064*0Sstevel@tonic-gate 		 * check memory
1065*0Sstevel@tonic-gate 		 */
1066*0Sstevel@tonic-gate 		size = all->n_ppdata * sizeof (struct di_priv_format);
1067*0Sstevel@tonic-gate 		off = di_checkmem(st, off, size);
1068*0Sstevel@tonic-gate 		all->ppdata_format = off;
1069*0Sstevel@tonic-gate 		if (ddi_copyin(priv->parent, di_mem_addr(st, off), size,
1070*0Sstevel@tonic-gate 		    mode) != 0) {
1071*0Sstevel@tonic-gate 			kmem_free(priv, sizeof (struct di_priv_data));
1072*0Sstevel@tonic-gate 			return (0);
1073*0Sstevel@tonic-gate 		}
1074*0Sstevel@tonic-gate 
1075*0Sstevel@tonic-gate 		off += size;
1076*0Sstevel@tonic-gate 	}
1077*0Sstevel@tonic-gate 
1078*0Sstevel@tonic-gate 	if (all->n_dpdata) {	/* driver private data format */
1079*0Sstevel@tonic-gate 		/*
1080*0Sstevel@tonic-gate 		 * check memory
1081*0Sstevel@tonic-gate 		 */
1082*0Sstevel@tonic-gate 		size = all->n_dpdata * sizeof (struct di_priv_format);
1083*0Sstevel@tonic-gate 		off = di_checkmem(st, off, size);
1084*0Sstevel@tonic-gate 		all->dpdata_format = off;
1085*0Sstevel@tonic-gate 		if (ddi_copyin(priv->driver, di_mem_addr(st, off), size,
1086*0Sstevel@tonic-gate 		    mode) != 0) {
1087*0Sstevel@tonic-gate 			kmem_free(priv, sizeof (struct di_priv_data));
1088*0Sstevel@tonic-gate 			return (0);
1089*0Sstevel@tonic-gate 		}
1090*0Sstevel@tonic-gate 
1091*0Sstevel@tonic-gate 		off += size;
1092*0Sstevel@tonic-gate 	}
1093*0Sstevel@tonic-gate 
1094*0Sstevel@tonic-gate 	kmem_free(priv, sizeof (struct di_priv_data));
1095*0Sstevel@tonic-gate 	return (off);
1096*0Sstevel@tonic-gate }
1097*0Sstevel@tonic-gate 
1098*0Sstevel@tonic-gate /*
1099*0Sstevel@tonic-gate  * Return the real address based on the offset (off) within snapshot
1100*0Sstevel@tonic-gate  */
1101*0Sstevel@tonic-gate static caddr_t
1102*0Sstevel@tonic-gate di_mem_addr(struct di_state *st, di_off_t off)
1103*0Sstevel@tonic-gate {
1104*0Sstevel@tonic-gate 	struct di_mem *dcp = st->memlist;
1105*0Sstevel@tonic-gate 
1106*0Sstevel@tonic-gate 	dcmn_err3((CE_CONT, "di_mem_addr: dcp=%p off=%x\n",
1107*0Sstevel@tonic-gate 		(void *)dcp, off));
1108*0Sstevel@tonic-gate 
1109*0Sstevel@tonic-gate 	ASSERT(off < st->mem_size);
1110*0Sstevel@tonic-gate 
1111*0Sstevel@tonic-gate 	while (off >= dcp->buf_size) {
1112*0Sstevel@tonic-gate 		off -= dcp->buf_size;
1113*0Sstevel@tonic-gate 		dcp = dcp->next;
1114*0Sstevel@tonic-gate 	}
1115*0Sstevel@tonic-gate 
1116*0Sstevel@tonic-gate 	dcmn_err3((CE_CONT, "di_mem_addr: new off=%x, return = %p\n",
1117*0Sstevel@tonic-gate 		off, (void *)(dcp->buf + off)));
1118*0Sstevel@tonic-gate 
1119*0Sstevel@tonic-gate 	return (dcp->buf + off);
1120*0Sstevel@tonic-gate }
1121*0Sstevel@tonic-gate 
1122*0Sstevel@tonic-gate /*
1123*0Sstevel@tonic-gate  * Ideally we would use the whole key to derive the hash
1124*0Sstevel@tonic-gate  * value. However, the probability that two keys will
1125*0Sstevel@tonic-gate  * have the same dip (or pip) is very low, so
1126*0Sstevel@tonic-gate  * hashing by dip (or pip) pointer should suffice.
1127*0Sstevel@tonic-gate  */
1128*0Sstevel@tonic-gate static uint_t
1129*0Sstevel@tonic-gate di_hash_byptr(void *arg, mod_hash_key_t key)
1130*0Sstevel@tonic-gate {
1131*0Sstevel@tonic-gate 	struct di_key *dik = key;
1132*0Sstevel@tonic-gate 	size_t rshift;
1133*0Sstevel@tonic-gate 	void *ptr;
1134*0Sstevel@tonic-gate 
1135*0Sstevel@tonic-gate 	ASSERT(arg == NULL);
1136*0Sstevel@tonic-gate 
1137*0Sstevel@tonic-gate 	switch (dik->k_type) {
1138*0Sstevel@tonic-gate 	case DI_DKEY:
1139*0Sstevel@tonic-gate 		ptr = dik->k_u.dkey.dk_dip;
1140*0Sstevel@tonic-gate 		rshift = highbit(sizeof (struct dev_info));
1141*0Sstevel@tonic-gate 		break;
1142*0Sstevel@tonic-gate 	case DI_PKEY:
1143*0Sstevel@tonic-gate 		ptr = dik->k_u.pkey.pk_pip;
1144*0Sstevel@tonic-gate 		rshift = highbit(sizeof (struct mdi_pathinfo));
1145*0Sstevel@tonic-gate 		break;
1146*0Sstevel@tonic-gate 	default:
1147*0Sstevel@tonic-gate 		panic("devinfo: unknown key type");
1148*0Sstevel@tonic-gate 		/*NOTREACHED*/
1149*0Sstevel@tonic-gate 	}
1150*0Sstevel@tonic-gate 	return (mod_hash_byptr((void *)rshift, ptr));
1151*0Sstevel@tonic-gate }
1152*0Sstevel@tonic-gate 
1153*0Sstevel@tonic-gate static void
1154*0Sstevel@tonic-gate di_key_dtor(mod_hash_key_t key)
1155*0Sstevel@tonic-gate {
1156*0Sstevel@tonic-gate 	char		*path_addr;
1157*0Sstevel@tonic-gate 	struct di_key	*dik = key;
1158*0Sstevel@tonic-gate 
1159*0Sstevel@tonic-gate 	switch (dik->k_type) {
1160*0Sstevel@tonic-gate 	case DI_DKEY:
1161*0Sstevel@tonic-gate 		break;
1162*0Sstevel@tonic-gate 	case DI_PKEY:
1163*0Sstevel@tonic-gate 		path_addr = dik->k_u.pkey.pk_path_addr;
1164*0Sstevel@tonic-gate 		if (path_addr)
1165*0Sstevel@tonic-gate 			kmem_free(path_addr, strlen(path_addr) + 1);
1166*0Sstevel@tonic-gate 		break;
1167*0Sstevel@tonic-gate 	default:
1168*0Sstevel@tonic-gate 		panic("devinfo: unknown key type");
1169*0Sstevel@tonic-gate 		/*NOTREACHED*/
1170*0Sstevel@tonic-gate 	}
1171*0Sstevel@tonic-gate 
1172*0Sstevel@tonic-gate 	kmem_free(dik, sizeof (struct di_key));
1173*0Sstevel@tonic-gate }
1174*0Sstevel@tonic-gate 
1175*0Sstevel@tonic-gate static int
1176*0Sstevel@tonic-gate di_dkey_cmp(struct di_dkey *dk1, struct di_dkey *dk2)
1177*0Sstevel@tonic-gate {
1178*0Sstevel@tonic-gate 	if (dk1->dk_dip !=  dk2->dk_dip)
1179*0Sstevel@tonic-gate 		return (dk1->dk_dip > dk2->dk_dip ? 1 : -1);
1180*0Sstevel@tonic-gate 
1181*0Sstevel@tonic-gate 	if (dk1->dk_major != -1 && dk2->dk_major != -1) {
1182*0Sstevel@tonic-gate 		if (dk1->dk_major !=  dk2->dk_major)
1183*0Sstevel@tonic-gate 			return (dk1->dk_major > dk2->dk_major ? 1 : -1);
1184*0Sstevel@tonic-gate 
1185*0Sstevel@tonic-gate 		if (dk1->dk_inst !=  dk2->dk_inst)
1186*0Sstevel@tonic-gate 			return (dk1->dk_inst > dk2->dk_inst ? 1 : -1);
1187*0Sstevel@tonic-gate 	}
1188*0Sstevel@tonic-gate 
1189*0Sstevel@tonic-gate 	if (dk1->dk_nodeid != dk2->dk_nodeid)
1190*0Sstevel@tonic-gate 		return (dk1->dk_nodeid > dk2->dk_nodeid ? 1 : -1);
1191*0Sstevel@tonic-gate 
1192*0Sstevel@tonic-gate 	return (0);
1193*0Sstevel@tonic-gate }
1194*0Sstevel@tonic-gate 
1195*0Sstevel@tonic-gate static int
1196*0Sstevel@tonic-gate di_pkey_cmp(struct di_pkey *pk1, struct di_pkey *pk2)
1197*0Sstevel@tonic-gate {
1198*0Sstevel@tonic-gate 	char *p1, *p2;
1199*0Sstevel@tonic-gate 	int rv;
1200*0Sstevel@tonic-gate 
1201*0Sstevel@tonic-gate 	if (pk1->pk_pip !=  pk2->pk_pip)
1202*0Sstevel@tonic-gate 		return (pk1->pk_pip > pk2->pk_pip ? 1 : -1);
1203*0Sstevel@tonic-gate 
1204*0Sstevel@tonic-gate 	p1 = pk1->pk_path_addr;
1205*0Sstevel@tonic-gate 	p2 = pk2->pk_path_addr;
1206*0Sstevel@tonic-gate 
1207*0Sstevel@tonic-gate 	p1 = p1 ? p1 : "";
1208*0Sstevel@tonic-gate 	p2 = p2 ? p2 : "";
1209*0Sstevel@tonic-gate 
1210*0Sstevel@tonic-gate 	rv = strcmp(p1, p2);
1211*0Sstevel@tonic-gate 	if (rv)
1212*0Sstevel@tonic-gate 		return (rv > 0  ? 1 : -1);
1213*0Sstevel@tonic-gate 
1214*0Sstevel@tonic-gate 	if (pk1->pk_client !=  pk2->pk_client)
1215*0Sstevel@tonic-gate 		return (pk1->pk_client > pk2->pk_client ? 1 : -1);
1216*0Sstevel@tonic-gate 
1217*0Sstevel@tonic-gate 	if (pk1->pk_phci !=  pk2->pk_phci)
1218*0Sstevel@tonic-gate 		return (pk1->pk_phci > pk2->pk_phci ? 1 : -1);
1219*0Sstevel@tonic-gate 
1220*0Sstevel@tonic-gate 	return (0);
1221*0Sstevel@tonic-gate }
1222*0Sstevel@tonic-gate 
1223*0Sstevel@tonic-gate static int
1224*0Sstevel@tonic-gate di_key_cmp(mod_hash_key_t key1, mod_hash_key_t key2)
1225*0Sstevel@tonic-gate {
1226*0Sstevel@tonic-gate 	struct di_key *dik1, *dik2;
1227*0Sstevel@tonic-gate 
1228*0Sstevel@tonic-gate 	dik1 = key1;
1229*0Sstevel@tonic-gate 	dik2 = key2;
1230*0Sstevel@tonic-gate 
1231*0Sstevel@tonic-gate 	if (dik1->k_type != dik2->k_type) {
1232*0Sstevel@tonic-gate 		panic("devinfo: mismatched keys");
1233*0Sstevel@tonic-gate 		/*NOTREACHED*/
1234*0Sstevel@tonic-gate 	}
1235*0Sstevel@tonic-gate 
1236*0Sstevel@tonic-gate 	switch (dik1->k_type) {
1237*0Sstevel@tonic-gate 	case DI_DKEY:
1238*0Sstevel@tonic-gate 		return (di_dkey_cmp(&(dik1->k_u.dkey), &(dik2->k_u.dkey)));
1239*0Sstevel@tonic-gate 	case DI_PKEY:
1240*0Sstevel@tonic-gate 		return (di_pkey_cmp(&(dik1->k_u.pkey), &(dik2->k_u.pkey)));
1241*0Sstevel@tonic-gate 	default:
1242*0Sstevel@tonic-gate 		panic("devinfo: unknown key type");
1243*0Sstevel@tonic-gate 		/*NOTREACHED*/
1244*0Sstevel@tonic-gate 	}
1245*0Sstevel@tonic-gate }
1246*0Sstevel@tonic-gate 
1247*0Sstevel@tonic-gate /*
1248*0Sstevel@tonic-gate  * This is the main function that takes a snapshot
1249*0Sstevel@tonic-gate  */
1250*0Sstevel@tonic-gate static di_off_t
1251*0Sstevel@tonic-gate di_snapshot(struct di_state *st)
1252*0Sstevel@tonic-gate {
1253*0Sstevel@tonic-gate 	di_off_t off;
1254*0Sstevel@tonic-gate 	struct di_all *all;
1255*0Sstevel@tonic-gate 	dev_info_t *rootnode;
1256*0Sstevel@tonic-gate 	char buf[80];
1257*0Sstevel@tonic-gate 
1258*0Sstevel@tonic-gate 	all = (struct di_all *)di_mem_addr(st, 0);
1259*0Sstevel@tonic-gate 	dcmn_err((CE_CONT, "Taking a snapshot of devinfo tree...\n"));
1260*0Sstevel@tonic-gate 
1261*0Sstevel@tonic-gate 	/*
1262*0Sstevel@tonic-gate 	 * Hold the devinfo node referred by the path.
1263*0Sstevel@tonic-gate 	 */
1264*0Sstevel@tonic-gate 	rootnode = e_ddi_hold_devi_by_path(all->root_path, 0);
1265*0Sstevel@tonic-gate 	if (rootnode == NULL) {
1266*0Sstevel@tonic-gate 		dcmn_err((CE_CONT, "Devinfo node %s not found\n",
1267*0Sstevel@tonic-gate 		    all->root_path));
1268*0Sstevel@tonic-gate 		return (0);
1269*0Sstevel@tonic-gate 	}
1270*0Sstevel@tonic-gate 
1271*0Sstevel@tonic-gate 	(void) snprintf(buf, sizeof (buf),
1272*0Sstevel@tonic-gate 	    "devinfo registered dips (statep=%p)", (void *)st);
1273*0Sstevel@tonic-gate 
1274*0Sstevel@tonic-gate 	st->reg_dip_hash = mod_hash_create_extended(buf, 64,
1275*0Sstevel@tonic-gate 	    di_key_dtor, mod_hash_null_valdtor, di_hash_byptr,
1276*0Sstevel@tonic-gate 	    NULL, di_key_cmp, KM_SLEEP);
1277*0Sstevel@tonic-gate 
1278*0Sstevel@tonic-gate 
1279*0Sstevel@tonic-gate 	(void) snprintf(buf, sizeof (buf),
1280*0Sstevel@tonic-gate 	    "devinfo registered pips (statep=%p)", (void *)st);
1281*0Sstevel@tonic-gate 
1282*0Sstevel@tonic-gate 	st->reg_pip_hash = mod_hash_create_extended(buf, 64,
1283*0Sstevel@tonic-gate 	    di_key_dtor, mod_hash_null_valdtor, di_hash_byptr,
1284*0Sstevel@tonic-gate 	    NULL, di_key_cmp, KM_SLEEP);
1285*0Sstevel@tonic-gate 
1286*0Sstevel@tonic-gate 	/*
1287*0Sstevel@tonic-gate 	 * copy the device tree
1288*0Sstevel@tonic-gate 	 */
1289*0Sstevel@tonic-gate 	off = di_copytree(DEVI(rootnode), &all->top_devinfo, st);
1290*0Sstevel@tonic-gate 
1291*0Sstevel@tonic-gate 	ddi_release_devi(rootnode);
1292*0Sstevel@tonic-gate 
1293*0Sstevel@tonic-gate 	/*
1294*0Sstevel@tonic-gate 	 * copy the devnames array
1295*0Sstevel@tonic-gate 	 */
1296*0Sstevel@tonic-gate 	all->devnames = off;
1297*0Sstevel@tonic-gate 	off = di_copydevnm(&all->devnames, st);
1298*0Sstevel@tonic-gate 
1299*0Sstevel@tonic-gate 
1300*0Sstevel@tonic-gate 	/* initialize the hash tables */
1301*0Sstevel@tonic-gate 	st->lnode_count = 0;
1302*0Sstevel@tonic-gate 	st->link_count = 0;
1303*0Sstevel@tonic-gate 
1304*0Sstevel@tonic-gate 	if (DINFOLYR & st->command) {
1305*0Sstevel@tonic-gate 		off = di_getlink_data(off, st);
1306*0Sstevel@tonic-gate 	}
1307*0Sstevel@tonic-gate 
1308*0Sstevel@tonic-gate 	/*
1309*0Sstevel@tonic-gate 	 * Free up hash tables
1310*0Sstevel@tonic-gate 	 */
1311*0Sstevel@tonic-gate 	mod_hash_destroy_hash(st->reg_dip_hash);
1312*0Sstevel@tonic-gate 	mod_hash_destroy_hash(st->reg_pip_hash);
1313*0Sstevel@tonic-gate 
1314*0Sstevel@tonic-gate 	/*
1315*0Sstevel@tonic-gate 	 * Record the timestamp now that we are done with snapshot.
1316*0Sstevel@tonic-gate 	 *
1317*0Sstevel@tonic-gate 	 * We compute the checksum later and then only if we cache
1318*0Sstevel@tonic-gate 	 * the snapshot, since checksumming adds some overhead.
1319*0Sstevel@tonic-gate 	 * The checksum is checked later if we read the cache file.
1320*0Sstevel@tonic-gate 	 * from disk.
1321*0Sstevel@tonic-gate 	 *
1322*0Sstevel@tonic-gate 	 * Set checksum field to 0 as CRC is calculated with that
1323*0Sstevel@tonic-gate 	 * field set to 0.
1324*0Sstevel@tonic-gate 	 */
1325*0Sstevel@tonic-gate 	all->snapshot_time = ddi_get_time();
1326*0Sstevel@tonic-gate 	all->cache_checksum = 0;
1327*0Sstevel@tonic-gate 
1328*0Sstevel@tonic-gate 	return (off);
1329*0Sstevel@tonic-gate }
1330*0Sstevel@tonic-gate 
1331*0Sstevel@tonic-gate /*
1332*0Sstevel@tonic-gate  * Assumes all devinfo nodes in device tree have been snapshotted
1333*0Sstevel@tonic-gate  */
1334*0Sstevel@tonic-gate static void
1335*0Sstevel@tonic-gate snap_driver_list(struct di_state *st, struct devnames *dnp, di_off_t *poff_p)
1336*0Sstevel@tonic-gate {
1337*0Sstevel@tonic-gate 	struct dev_info *node;
1338*0Sstevel@tonic-gate 	struct di_node *me;
1339*0Sstevel@tonic-gate 	di_off_t off;
1340*0Sstevel@tonic-gate 
1341*0Sstevel@tonic-gate 	ASSERT(mutex_owned(&dnp->dn_lock));
1342*0Sstevel@tonic-gate 
1343*0Sstevel@tonic-gate 	node = DEVI(dnp->dn_head);
1344*0Sstevel@tonic-gate 	for (; node; node = node->devi_next) {
1345*0Sstevel@tonic-gate 		if (di_dip_find(st, (dev_info_t *)node, &off) != 0)
1346*0Sstevel@tonic-gate 			continue;
1347*0Sstevel@tonic-gate 
1348*0Sstevel@tonic-gate 		ASSERT(off > 0);
1349*0Sstevel@tonic-gate 		me = (struct di_node *)di_mem_addr(st, off);
1350*0Sstevel@tonic-gate 		ASSERT(me->next == 0 || me->next == -1);
1351*0Sstevel@tonic-gate 		/*
1352*0Sstevel@tonic-gate 		 * Only nodes which were BOUND when they were
1353*0Sstevel@tonic-gate 		 * snapshotted will be added to per-driver list.
1354*0Sstevel@tonic-gate 		 */
1355*0Sstevel@tonic-gate 		if (me->next != -1)
1356*0Sstevel@tonic-gate 			continue;
1357*0Sstevel@tonic-gate 
1358*0Sstevel@tonic-gate 		*poff_p = off;
1359*0Sstevel@tonic-gate 		poff_p = &me->next;
1360*0Sstevel@tonic-gate 	}
1361*0Sstevel@tonic-gate 
1362*0Sstevel@tonic-gate 	*poff_p = 0;
1363*0Sstevel@tonic-gate }
1364*0Sstevel@tonic-gate 
1365*0Sstevel@tonic-gate /*
1366*0Sstevel@tonic-gate  * Copy the devnames array, so we have a list of drivers in the snapshot.
1367*0Sstevel@tonic-gate  * Also makes it possible to locate the per-driver devinfo nodes.
1368*0Sstevel@tonic-gate  */
1369*0Sstevel@tonic-gate static di_off_t
1370*0Sstevel@tonic-gate di_copydevnm(di_off_t *off_p, struct di_state *st)
1371*0Sstevel@tonic-gate {
1372*0Sstevel@tonic-gate 	int i;
1373*0Sstevel@tonic-gate 	di_off_t off;
1374*0Sstevel@tonic-gate 	size_t size;
1375*0Sstevel@tonic-gate 	struct di_devnm *dnp;
1376*0Sstevel@tonic-gate 
1377*0Sstevel@tonic-gate 	dcmn_err2((CE_CONT, "di_copydevnm: *off_p = %p\n", (void *)off_p));
1378*0Sstevel@tonic-gate 
1379*0Sstevel@tonic-gate 	/*
1380*0Sstevel@tonic-gate 	 * make sure there is some allocated memory
1381*0Sstevel@tonic-gate 	 */
1382*0Sstevel@tonic-gate 	size = devcnt * sizeof (struct di_devnm);
1383*0Sstevel@tonic-gate 	off = di_checkmem(st, *off_p, size);
1384*0Sstevel@tonic-gate 	*off_p = off;
1385*0Sstevel@tonic-gate 
1386*0Sstevel@tonic-gate 	dcmn_err((CE_CONT, "Start copying devnamesp[%d] at offset 0x%x\n",
1387*0Sstevel@tonic-gate 		devcnt, off));
1388*0Sstevel@tonic-gate 
1389*0Sstevel@tonic-gate 	dnp = (struct di_devnm *)di_mem_addr(st, off);
1390*0Sstevel@tonic-gate 	off += size;
1391*0Sstevel@tonic-gate 
1392*0Sstevel@tonic-gate 	for (i = 0; i < devcnt; i++) {
1393*0Sstevel@tonic-gate 		if (devnamesp[i].dn_name == NULL) {
1394*0Sstevel@tonic-gate 			continue;
1395*0Sstevel@tonic-gate 		}
1396*0Sstevel@tonic-gate 
1397*0Sstevel@tonic-gate 		/*
1398*0Sstevel@tonic-gate 		 * dn_name is not freed during driver unload or removal.
1399*0Sstevel@tonic-gate 		 *
1400*0Sstevel@tonic-gate 		 * There is a race condition when make_devname() changes
1401*0Sstevel@tonic-gate 		 * dn_name during our strcpy. This should be rare since
1402*0Sstevel@tonic-gate 		 * only add_drv does this. At any rate, we never had a
1403*0Sstevel@tonic-gate 		 * problem with ddi_name_to_major(), which should have
1404*0Sstevel@tonic-gate 		 * the same problem.
1405*0Sstevel@tonic-gate 		 */
1406*0Sstevel@tonic-gate 		dcmn_err2((CE_CONT, "di_copydevnm: %s%d, off=%x\n",
1407*0Sstevel@tonic-gate 			devnamesp[i].dn_name, devnamesp[i].dn_instance,
1408*0Sstevel@tonic-gate 			off));
1409*0Sstevel@tonic-gate 
1410*0Sstevel@tonic-gate 		off = di_checkmem(st, off, strlen(devnamesp[i].dn_name) + 1);
1411*0Sstevel@tonic-gate 		dnp[i].name = off;
1412*0Sstevel@tonic-gate 		(void) strcpy((char *)di_mem_addr(st, off),
1413*0Sstevel@tonic-gate 			devnamesp[i].dn_name);
1414*0Sstevel@tonic-gate 		off += DI_ALIGN(strlen(devnamesp[i].dn_name) + 1);
1415*0Sstevel@tonic-gate 
1416*0Sstevel@tonic-gate 		mutex_enter(&devnamesp[i].dn_lock);
1417*0Sstevel@tonic-gate 
1418*0Sstevel@tonic-gate 		/*
1419*0Sstevel@tonic-gate 		 * Snapshot per-driver node list
1420*0Sstevel@tonic-gate 		 */
1421*0Sstevel@tonic-gate 		snap_driver_list(st, &devnamesp[i], &dnp[i].head);
1422*0Sstevel@tonic-gate 
1423*0Sstevel@tonic-gate 		/*
1424*0Sstevel@tonic-gate 		 * This is not used by libdevinfo, leave it for now
1425*0Sstevel@tonic-gate 		 */
1426*0Sstevel@tonic-gate 		dnp[i].flags = devnamesp[i].dn_flags;
1427*0Sstevel@tonic-gate 		dnp[i].instance = devnamesp[i].dn_instance;
1428*0Sstevel@tonic-gate 
1429*0Sstevel@tonic-gate 		/*
1430*0Sstevel@tonic-gate 		 * get global properties
1431*0Sstevel@tonic-gate 		 */
1432*0Sstevel@tonic-gate 		if ((DINFOPROP & st->command) &&
1433*0Sstevel@tonic-gate 		    devnamesp[i].dn_global_prop_ptr) {
1434*0Sstevel@tonic-gate 			dnp[i].global_prop = off;
1435*0Sstevel@tonic-gate 			off = di_getprop(
1436*0Sstevel@tonic-gate 			    devnamesp[i].dn_global_prop_ptr->prop_list,
1437*0Sstevel@tonic-gate 			    &dnp[i].global_prop, st, NULL, DI_PROP_GLB_LIST);
1438*0Sstevel@tonic-gate 		}
1439*0Sstevel@tonic-gate 
1440*0Sstevel@tonic-gate 		/*
1441*0Sstevel@tonic-gate 		 * Bit encode driver ops: & bus_ops, cb_ops, & cb_ops->cb_str
1442*0Sstevel@tonic-gate 		 */
1443*0Sstevel@tonic-gate 		if (CB_DRV_INSTALLED(devopsp[i])) {
1444*0Sstevel@tonic-gate 			if (devopsp[i]->devo_cb_ops) {
1445*0Sstevel@tonic-gate 				dnp[i].ops |= DI_CB_OPS;
1446*0Sstevel@tonic-gate 				if (devopsp[i]->devo_cb_ops->cb_str)
1447*0Sstevel@tonic-gate 					dnp[i].ops |= DI_STREAM_OPS;
1448*0Sstevel@tonic-gate 			}
1449*0Sstevel@tonic-gate 			if (NEXUS_DRV(devopsp[i])) {
1450*0Sstevel@tonic-gate 				dnp[i].ops |= DI_BUS_OPS;
1451*0Sstevel@tonic-gate 			}
1452*0Sstevel@tonic-gate 		}
1453*0Sstevel@tonic-gate 
1454*0Sstevel@tonic-gate 		mutex_exit(&devnamesp[i].dn_lock);
1455*0Sstevel@tonic-gate 	}
1456*0Sstevel@tonic-gate 
1457*0Sstevel@tonic-gate 	dcmn_err((CE_CONT, "End copying devnamesp at offset 0x%x\n", off));
1458*0Sstevel@tonic-gate 
1459*0Sstevel@tonic-gate 	return (off);
1460*0Sstevel@tonic-gate }
1461*0Sstevel@tonic-gate 
1462*0Sstevel@tonic-gate /*
1463*0Sstevel@tonic-gate  * Copy the kernel devinfo tree. The tree and the devnames array forms
1464*0Sstevel@tonic-gate  * the entire snapshot (see also di_copydevnm).
1465*0Sstevel@tonic-gate  */
1466*0Sstevel@tonic-gate static di_off_t
1467*0Sstevel@tonic-gate di_copytree(struct dev_info *root, di_off_t *off_p, struct di_state *st)
1468*0Sstevel@tonic-gate {
1469*0Sstevel@tonic-gate 	di_off_t off;
1470*0Sstevel@tonic-gate 	struct di_stack *dsp = kmem_zalloc(sizeof (struct di_stack), KM_SLEEP);
1471*0Sstevel@tonic-gate 
1472*0Sstevel@tonic-gate 	dcmn_err((CE_CONT, "di_copytree: root = %p, *off_p = %x\n",
1473*0Sstevel@tonic-gate 		(void *)root, *off_p));
1474*0Sstevel@tonic-gate 
1475*0Sstevel@tonic-gate 	/* force attach drivers */
1476*0Sstevel@tonic-gate 	if ((i_ddi_node_state((dev_info_t *)root) == DS_READY) &&
1477*0Sstevel@tonic-gate 	    (st->command & DINFOSUBTREE) && (st->command & DINFOFORCE)) {
1478*0Sstevel@tonic-gate 		(void) ndi_devi_config((dev_info_t *)root,
1479*0Sstevel@tonic-gate 		    NDI_CONFIG | NDI_DEVI_PERSIST | NDI_NO_EVENT |
1480*0Sstevel@tonic-gate 		    NDI_DRV_CONF_REPROBE);
1481*0Sstevel@tonic-gate 	}
1482*0Sstevel@tonic-gate 
1483*0Sstevel@tonic-gate 	/*
1484*0Sstevel@tonic-gate 	 * Push top_devinfo onto a stack
1485*0Sstevel@tonic-gate 	 *
1486*0Sstevel@tonic-gate 	 * The stack is necessary to avoid recursion, which can overrun
1487*0Sstevel@tonic-gate 	 * the kernel stack.
1488*0Sstevel@tonic-gate 	 */
1489*0Sstevel@tonic-gate 	PUSH_STACK(dsp, root, off_p);
1490*0Sstevel@tonic-gate 
1491*0Sstevel@tonic-gate 	/*
1492*0Sstevel@tonic-gate 	 * As long as there is a node on the stack, copy the node.
1493*0Sstevel@tonic-gate 	 * di_copynode() is responsible for pushing and popping
1494*0Sstevel@tonic-gate 	 * child and sibling nodes on the stack.
1495*0Sstevel@tonic-gate 	 */
1496*0Sstevel@tonic-gate 	while (!EMPTY_STACK(dsp)) {
1497*0Sstevel@tonic-gate 		off = di_copynode(dsp, st);
1498*0Sstevel@tonic-gate 	}
1499*0Sstevel@tonic-gate 
1500*0Sstevel@tonic-gate 	/*
1501*0Sstevel@tonic-gate 	 * Free the stack structure
1502*0Sstevel@tonic-gate 	 */
1503*0Sstevel@tonic-gate 	kmem_free(dsp, sizeof (struct di_stack));
1504*0Sstevel@tonic-gate 
1505*0Sstevel@tonic-gate 	return (off);
1506*0Sstevel@tonic-gate }
1507*0Sstevel@tonic-gate 
1508*0Sstevel@tonic-gate /*
1509*0Sstevel@tonic-gate  * This is the core function, which copies all data associated with a single
1510*0Sstevel@tonic-gate  * node into the snapshot. The amount of information is determined by the
1511*0Sstevel@tonic-gate  * ioctl command.
1512*0Sstevel@tonic-gate  */
1513*0Sstevel@tonic-gate static di_off_t
1514*0Sstevel@tonic-gate di_copynode(struct di_stack *dsp, struct di_state *st)
1515*0Sstevel@tonic-gate {
1516*0Sstevel@tonic-gate 	di_off_t off;
1517*0Sstevel@tonic-gate 	struct di_node *me;
1518*0Sstevel@tonic-gate 	struct dev_info *node;
1519*0Sstevel@tonic-gate 
1520*0Sstevel@tonic-gate 	dcmn_err2((CE_CONT, "di_copynode: depth = %x\n",
1521*0Sstevel@tonic-gate 			dsp->depth));
1522*0Sstevel@tonic-gate 
1523*0Sstevel@tonic-gate 	node = TOP_NODE(dsp);
1524*0Sstevel@tonic-gate 
1525*0Sstevel@tonic-gate 	ASSERT(node != NULL);
1526*0Sstevel@tonic-gate 
1527*0Sstevel@tonic-gate 	/*
1528*0Sstevel@tonic-gate 	 * check memory usage, and fix offsets accordingly.
1529*0Sstevel@tonic-gate 	 */
1530*0Sstevel@tonic-gate 	off = di_checkmem(st, *(TOP_OFFSET(dsp)), sizeof (struct di_node));
1531*0Sstevel@tonic-gate 	*(TOP_OFFSET(dsp)) = off;
1532*0Sstevel@tonic-gate 	me = DI_NODE(di_mem_addr(st, off));
1533*0Sstevel@tonic-gate 
1534*0Sstevel@tonic-gate 	dcmn_err((CE_CONT, "copy node %s, instance #%d, at offset 0x%x\n",
1535*0Sstevel@tonic-gate 			node->devi_node_name, node->devi_instance, off));
1536*0Sstevel@tonic-gate 
1537*0Sstevel@tonic-gate 	/*
1538*0Sstevel@tonic-gate 	 * Node parameters:
1539*0Sstevel@tonic-gate 	 * self		-- offset of current node within snapshot
1540*0Sstevel@tonic-gate 	 * nodeid	-- pointer to PROM node (tri-valued)
1541*0Sstevel@tonic-gate 	 * state	-- hot plugging device state
1542*0Sstevel@tonic-gate 	 * node_state	-- devinfo node state (CF1, CF2, etc.)
1543*0Sstevel@tonic-gate 	 */
1544*0Sstevel@tonic-gate 	me->self = off;
1545*0Sstevel@tonic-gate 	me->instance = node->devi_instance;
1546*0Sstevel@tonic-gate 	me->nodeid = node->devi_nodeid;
1547*0Sstevel@tonic-gate 	me->node_class = node->devi_node_class;
1548*0Sstevel@tonic-gate 	me->attributes = node->devi_node_attributes;
1549*0Sstevel@tonic-gate 	me->state = node->devi_state;
1550*0Sstevel@tonic-gate 	me->node_state = node->devi_node_state;
1551*0Sstevel@tonic-gate 	me->user_private_data = NULL;
1552*0Sstevel@tonic-gate 
1553*0Sstevel@tonic-gate 	/*
1554*0Sstevel@tonic-gate 	 * Get parent's offset in snapshot from the stack
1555*0Sstevel@tonic-gate 	 * and store it in the current node
1556*0Sstevel@tonic-gate 	 */
1557*0Sstevel@tonic-gate 	if (dsp->depth > 1) {
1558*0Sstevel@tonic-gate 		me->parent = *(PARENT_OFFSET(dsp));
1559*0Sstevel@tonic-gate 	}
1560*0Sstevel@tonic-gate 
1561*0Sstevel@tonic-gate 	/*
1562*0Sstevel@tonic-gate 	 * Save the offset of this di_node in a hash table.
1563*0Sstevel@tonic-gate 	 * This is used later to resolve references to this
1564*0Sstevel@tonic-gate 	 * dip from other parts of the tree (per-driver list,
1565*0Sstevel@tonic-gate 	 * multipathing linkages, layered usage linkages).
1566*0Sstevel@tonic-gate 	 * The key used for the hash table is derived from
1567*0Sstevel@tonic-gate 	 * information in the dip.
1568*0Sstevel@tonic-gate 	 */
1569*0Sstevel@tonic-gate 	di_register_dip(st, (dev_info_t *)node, me->self);
1570*0Sstevel@tonic-gate 
1571*0Sstevel@tonic-gate 	/*
1572*0Sstevel@tonic-gate 	 * increment offset
1573*0Sstevel@tonic-gate 	 */
1574*0Sstevel@tonic-gate 	off += sizeof (struct di_node);
1575*0Sstevel@tonic-gate 
1576*0Sstevel@tonic-gate #ifdef	DEVID_COMPATIBILITY
1577*0Sstevel@tonic-gate 	/* check for devid as property marker */
1578*0Sstevel@tonic-gate 	if (node->devi_devid) {
1579*0Sstevel@tonic-gate 		ddi_devid_t	devid;
1580*0Sstevel@tonic-gate 		char 		*devidstr;
1581*0Sstevel@tonic-gate 		int		devid_size;
1582*0Sstevel@tonic-gate 
1583*0Sstevel@tonic-gate 		/*
1584*0Sstevel@tonic-gate 		 * The devid is now represented as a property.
1585*0Sstevel@tonic-gate 		 * For micro release compatibility with di_devid interface
1586*0Sstevel@tonic-gate 		 * in libdevinfo we must return it as a binary structure in'
1587*0Sstevel@tonic-gate 		 * the snapshot.  When di_devid is removed from libdevinfo
1588*0Sstevel@tonic-gate 		 * in a future release (and devi_devid is deleted) then
1589*0Sstevel@tonic-gate 		 * code related to DEVID_COMPATIBILITY can be removed.
1590*0Sstevel@tonic-gate 		 */
1591*0Sstevel@tonic-gate 		ASSERT(node->devi_devid == DEVID_COMPATIBILITY);
1592*0Sstevel@tonic-gate /* XXX should be DDI_DEV_T_NONE! */
1593*0Sstevel@tonic-gate 		if (ddi_prop_lookup_string(DDI_DEV_T_ANY, (dev_info_t *)node,
1594*0Sstevel@tonic-gate 		    DDI_PROP_DONTPASS, DEVID_PROP_NAME, &devidstr) ==
1595*0Sstevel@tonic-gate 		    DDI_PROP_SUCCESS) {
1596*0Sstevel@tonic-gate 			if (ddi_devid_str_decode(devidstr, &devid, NULL) ==
1597*0Sstevel@tonic-gate 			    DDI_SUCCESS) {
1598*0Sstevel@tonic-gate 				devid_size = ddi_devid_sizeof(devid);
1599*0Sstevel@tonic-gate 				off = di_checkmem(st, off, devid_size);
1600*0Sstevel@tonic-gate 				me->devid = off;
1601*0Sstevel@tonic-gate 				bcopy(devid,
1602*0Sstevel@tonic-gate 				    di_mem_addr(st, off), devid_size);
1603*0Sstevel@tonic-gate 				off += devid_size;
1604*0Sstevel@tonic-gate 				ddi_devid_free(devid);
1605*0Sstevel@tonic-gate 			}
1606*0Sstevel@tonic-gate 			ddi_prop_free(devidstr);
1607*0Sstevel@tonic-gate 		}
1608*0Sstevel@tonic-gate 	}
1609*0Sstevel@tonic-gate #endif	/* DEVID_COMPATIBILITY */
1610*0Sstevel@tonic-gate 
1611*0Sstevel@tonic-gate 	if (node->devi_node_name) {
1612*0Sstevel@tonic-gate 		off = di_checkmem(st, off, strlen(node->devi_node_name) + 1);
1613*0Sstevel@tonic-gate 		me->node_name = off;
1614*0Sstevel@tonic-gate 		(void) strcpy(di_mem_addr(st, off), node->devi_node_name);
1615*0Sstevel@tonic-gate 		off += strlen(node->devi_node_name) + 1;
1616*0Sstevel@tonic-gate 	}
1617*0Sstevel@tonic-gate 
1618*0Sstevel@tonic-gate 	if (node->devi_compat_names && (node->devi_compat_length > 1)) {
1619*0Sstevel@tonic-gate 		off = di_checkmem(st, off, node->devi_compat_length);
1620*0Sstevel@tonic-gate 		me->compat_names = off;
1621*0Sstevel@tonic-gate 		me->compat_length = node->devi_compat_length;
1622*0Sstevel@tonic-gate 		bcopy(node->devi_compat_names, di_mem_addr(st, off),
1623*0Sstevel@tonic-gate 			node->devi_compat_length);
1624*0Sstevel@tonic-gate 		off += node->devi_compat_length;
1625*0Sstevel@tonic-gate 	}
1626*0Sstevel@tonic-gate 
1627*0Sstevel@tonic-gate 	if (node->devi_addr) {
1628*0Sstevel@tonic-gate 		off = di_checkmem(st, off, strlen(node->devi_addr) + 1);
1629*0Sstevel@tonic-gate 		me->address = off;
1630*0Sstevel@tonic-gate 		(void) strcpy(di_mem_addr(st, off), node->devi_addr);
1631*0Sstevel@tonic-gate 		off += strlen(node->devi_addr) + 1;
1632*0Sstevel@tonic-gate 	}
1633*0Sstevel@tonic-gate 
1634*0Sstevel@tonic-gate 	if (node->devi_binding_name) {
1635*0Sstevel@tonic-gate 		off = di_checkmem(st, off, strlen(node->devi_binding_name) + 1);
1636*0Sstevel@tonic-gate 		me->bind_name = off;
1637*0Sstevel@tonic-gate 		(void) strcpy(di_mem_addr(st, off), node->devi_binding_name);
1638*0Sstevel@tonic-gate 		off += strlen(node->devi_binding_name) + 1;
1639*0Sstevel@tonic-gate 	}
1640*0Sstevel@tonic-gate 
1641*0Sstevel@tonic-gate 	me->drv_major = node->devi_major;
1642*0Sstevel@tonic-gate 
1643*0Sstevel@tonic-gate 	/*
1644*0Sstevel@tonic-gate 	 * If the dip is BOUND, set the next pointer of the
1645*0Sstevel@tonic-gate 	 * per-instance list to -1, indicating that it is yet to be resolved.
1646*0Sstevel@tonic-gate 	 * This will be resolved later in snap_driver_list().
1647*0Sstevel@tonic-gate 	 */
1648*0Sstevel@tonic-gate 	if (me->drv_major != -1) {
1649*0Sstevel@tonic-gate 		me->next = -1;
1650*0Sstevel@tonic-gate 	} else {
1651*0Sstevel@tonic-gate 		me->next = 0;
1652*0Sstevel@tonic-gate 	}
1653*0Sstevel@tonic-gate 
1654*0Sstevel@tonic-gate 	/*
1655*0Sstevel@tonic-gate 	 * An optimization to skip mutex_enter when not needed.
1656*0Sstevel@tonic-gate 	 */
1657*0Sstevel@tonic-gate 	if (!((DINFOMINOR | DINFOPROP | DINFOPATH) & st->command)) {
1658*0Sstevel@tonic-gate 		goto priv_data;
1659*0Sstevel@tonic-gate 	}
1660*0Sstevel@tonic-gate 
1661*0Sstevel@tonic-gate 	/*
1662*0Sstevel@tonic-gate 	 * Grab current per dev_info node lock to
1663*0Sstevel@tonic-gate 	 * get minor data and properties.
1664*0Sstevel@tonic-gate 	 */
1665*0Sstevel@tonic-gate 	mutex_enter(&(node->devi_lock));
1666*0Sstevel@tonic-gate 
1667*0Sstevel@tonic-gate 	if (!(DINFOMINOR & st->command)) {
1668*0Sstevel@tonic-gate 		goto path;
1669*0Sstevel@tonic-gate 	}
1670*0Sstevel@tonic-gate 
1671*0Sstevel@tonic-gate 	if (node->devi_minor) {		/* minor data */
1672*0Sstevel@tonic-gate 		me->minor_data = DI_ALIGN(off);
1673*0Sstevel@tonic-gate 		off = di_getmdata(node->devi_minor, &me->minor_data,
1674*0Sstevel@tonic-gate 		    me->self, st);
1675*0Sstevel@tonic-gate 	}
1676*0Sstevel@tonic-gate 
1677*0Sstevel@tonic-gate path:
1678*0Sstevel@tonic-gate 	if (!(DINFOPATH & st->command)) {
1679*0Sstevel@tonic-gate 		goto property;
1680*0Sstevel@tonic-gate 	}
1681*0Sstevel@tonic-gate 
1682*0Sstevel@tonic-gate 	if (MDI_CLIENT(node)) {
1683*0Sstevel@tonic-gate 		me->multipath_client = DI_ALIGN(off);
1684*0Sstevel@tonic-gate 		off = di_getpath_data((dev_info_t *)node, &me->multipath_client,
1685*0Sstevel@tonic-gate 		    me->self, st, 1);
1686*0Sstevel@tonic-gate 		dcmn_err((CE_WARN, "me->multipath_client = %x for node %p "
1687*0Sstevel@tonic-gate 		    "component type = %d.  off=%d",
1688*0Sstevel@tonic-gate 		    me->multipath_client,
1689*0Sstevel@tonic-gate 		    (void *)node, node->devi_mdi_component, off));
1690*0Sstevel@tonic-gate 	}
1691*0Sstevel@tonic-gate 
1692*0Sstevel@tonic-gate 	if (MDI_PHCI(node)) {
1693*0Sstevel@tonic-gate 		me->multipath_phci = DI_ALIGN(off);
1694*0Sstevel@tonic-gate 		off = di_getpath_data((dev_info_t *)node, &me->multipath_phci,
1695*0Sstevel@tonic-gate 		    me->self, st, 0);
1696*0Sstevel@tonic-gate 		dcmn_err((CE_WARN, "me->multipath_phci = %x for node %p "
1697*0Sstevel@tonic-gate 		    "component type = %d.  off=%d",
1698*0Sstevel@tonic-gate 		    me->multipath_phci,
1699*0Sstevel@tonic-gate 		    (void *)node, node->devi_mdi_component, off));
1700*0Sstevel@tonic-gate 	}
1701*0Sstevel@tonic-gate 
1702*0Sstevel@tonic-gate property:
1703*0Sstevel@tonic-gate 	if (!(DINFOPROP & st->command)) {
1704*0Sstevel@tonic-gate 		goto unlock;
1705*0Sstevel@tonic-gate 	}
1706*0Sstevel@tonic-gate 
1707*0Sstevel@tonic-gate 	if (node->devi_drv_prop_ptr) {	/* driver property list */
1708*0Sstevel@tonic-gate 		me->drv_prop = DI_ALIGN(off);
1709*0Sstevel@tonic-gate 		off = di_getprop(node->devi_drv_prop_ptr, &me->drv_prop, st,
1710*0Sstevel@tonic-gate 			node, DI_PROP_DRV_LIST);
1711*0Sstevel@tonic-gate 	}
1712*0Sstevel@tonic-gate 
1713*0Sstevel@tonic-gate 	if (node->devi_sys_prop_ptr) {	/* system property list */
1714*0Sstevel@tonic-gate 		me->sys_prop = DI_ALIGN(off);
1715*0Sstevel@tonic-gate 		off = di_getprop(node->devi_sys_prop_ptr, &me->sys_prop, st,
1716*0Sstevel@tonic-gate 			node, DI_PROP_SYS_LIST);
1717*0Sstevel@tonic-gate 	}
1718*0Sstevel@tonic-gate 
1719*0Sstevel@tonic-gate 	if (node->devi_hw_prop_ptr) {	/* hardware property list */
1720*0Sstevel@tonic-gate 		me->hw_prop = DI_ALIGN(off);
1721*0Sstevel@tonic-gate 		off = di_getprop(node->devi_hw_prop_ptr, &me->hw_prop, st,
1722*0Sstevel@tonic-gate 			node, DI_PROP_HW_LIST);
1723*0Sstevel@tonic-gate 	}
1724*0Sstevel@tonic-gate 
1725*0Sstevel@tonic-gate 	if (node->devi_global_prop_list == NULL) {
1726*0Sstevel@tonic-gate 		me->glob_prop = (di_off_t)-1;	/* not global property */
1727*0Sstevel@tonic-gate 	} else {
1728*0Sstevel@tonic-gate 		/*
1729*0Sstevel@tonic-gate 		 * Make copy of global property list if this devinfo refers
1730*0Sstevel@tonic-gate 		 * global properties different from what's on the devnames
1731*0Sstevel@tonic-gate 		 * array. It can happen if there has been a forced
1732*0Sstevel@tonic-gate 		 * driver.conf update. See mod_drv(1M).
1733*0Sstevel@tonic-gate 		 */
1734*0Sstevel@tonic-gate 		ASSERT(me->drv_major != -1);
1735*0Sstevel@tonic-gate 		if (node->devi_global_prop_list !=
1736*0Sstevel@tonic-gate 		    devnamesp[me->drv_major].dn_global_prop_ptr) {
1737*0Sstevel@tonic-gate 			me->glob_prop = DI_ALIGN(off);
1738*0Sstevel@tonic-gate 			off = di_getprop(node->devi_global_prop_list->prop_list,
1739*0Sstevel@tonic-gate 			    &me->glob_prop, st, node, DI_PROP_GLB_LIST);
1740*0Sstevel@tonic-gate 		}
1741*0Sstevel@tonic-gate 	}
1742*0Sstevel@tonic-gate 
1743*0Sstevel@tonic-gate unlock:
1744*0Sstevel@tonic-gate 	/*
1745*0Sstevel@tonic-gate 	 * release current per dev_info node lock
1746*0Sstevel@tonic-gate 	 */
1747*0Sstevel@tonic-gate 	mutex_exit(&(node->devi_lock));
1748*0Sstevel@tonic-gate 
1749*0Sstevel@tonic-gate priv_data:
1750*0Sstevel@tonic-gate 	if (!(DINFOPRIVDATA & st->command)) {
1751*0Sstevel@tonic-gate 		goto pm_info;
1752*0Sstevel@tonic-gate 	}
1753*0Sstevel@tonic-gate 
1754*0Sstevel@tonic-gate 	if (ddi_get_parent_data((dev_info_t *)node) != NULL) {
1755*0Sstevel@tonic-gate 		me->parent_data = DI_ALIGN(off);
1756*0Sstevel@tonic-gate 		off = di_getppdata(node, &me->parent_data, st);
1757*0Sstevel@tonic-gate 	}
1758*0Sstevel@tonic-gate 
1759*0Sstevel@tonic-gate 	if (ddi_get_driver_private((dev_info_t *)node) != NULL) {
1760*0Sstevel@tonic-gate 		me->driver_data = DI_ALIGN(off);
1761*0Sstevel@tonic-gate 		off = di_getdpdata(node, &me->driver_data, st);
1762*0Sstevel@tonic-gate 	}
1763*0Sstevel@tonic-gate 
1764*0Sstevel@tonic-gate pm_info: /* NOT implemented */
1765*0Sstevel@tonic-gate 
1766*0Sstevel@tonic-gate subtree:
1767*0Sstevel@tonic-gate 	if (!(DINFOSUBTREE & st->command)) {
1768*0Sstevel@tonic-gate 		POP_STACK(dsp);
1769*0Sstevel@tonic-gate 		return (DI_ALIGN(off));
1770*0Sstevel@tonic-gate 	}
1771*0Sstevel@tonic-gate 
1772*0Sstevel@tonic-gate child:
1773*0Sstevel@tonic-gate 	/*
1774*0Sstevel@tonic-gate 	 * If there is a child--push child onto stack.
1775*0Sstevel@tonic-gate 	 * Hold the parent busy while doing so.
1776*0Sstevel@tonic-gate 	 */
1777*0Sstevel@tonic-gate 	if (node->devi_child) {
1778*0Sstevel@tonic-gate 		me->child = DI_ALIGN(off);
1779*0Sstevel@tonic-gate 		PUSH_STACK(dsp, node->devi_child, &me->child);
1780*0Sstevel@tonic-gate 		return (me->child);
1781*0Sstevel@tonic-gate 	}
1782*0Sstevel@tonic-gate 
1783*0Sstevel@tonic-gate sibling:
1784*0Sstevel@tonic-gate 	/*
1785*0Sstevel@tonic-gate 	 * no child node, unroll the stack till a sibling of
1786*0Sstevel@tonic-gate 	 * a parent node is found or root node is reached
1787*0Sstevel@tonic-gate 	 */
1788*0Sstevel@tonic-gate 	POP_STACK(dsp);
1789*0Sstevel@tonic-gate 	while (!EMPTY_STACK(dsp) && (node->devi_sibling == NULL)) {
1790*0Sstevel@tonic-gate 		node = TOP_NODE(dsp);
1791*0Sstevel@tonic-gate 		me = DI_NODE(di_mem_addr(st, *(TOP_OFFSET(dsp))));
1792*0Sstevel@tonic-gate 		POP_STACK(dsp);
1793*0Sstevel@tonic-gate 	}
1794*0Sstevel@tonic-gate 
1795*0Sstevel@tonic-gate 	if (!EMPTY_STACK(dsp)) {
1796*0Sstevel@tonic-gate 		/*
1797*0Sstevel@tonic-gate 		 * a sibling is found, replace top of stack by its sibling
1798*0Sstevel@tonic-gate 		 */
1799*0Sstevel@tonic-gate 		me->sibling = DI_ALIGN(off);
1800*0Sstevel@tonic-gate 		PUSH_STACK(dsp, node->devi_sibling, &me->sibling);
1801*0Sstevel@tonic-gate 		return (me->sibling);
1802*0Sstevel@tonic-gate 	}
1803*0Sstevel@tonic-gate 
1804*0Sstevel@tonic-gate 	/*
1805*0Sstevel@tonic-gate 	 * DONE with all nodes
1806*0Sstevel@tonic-gate 	 */
1807*0Sstevel@tonic-gate 	return (DI_ALIGN(off));
1808*0Sstevel@tonic-gate }
1809*0Sstevel@tonic-gate 
1810*0Sstevel@tonic-gate static i_lnode_t *
1811*0Sstevel@tonic-gate i_lnode_alloc(int modid)
1812*0Sstevel@tonic-gate {
1813*0Sstevel@tonic-gate 	i_lnode_t	*i_lnode;
1814*0Sstevel@tonic-gate 
1815*0Sstevel@tonic-gate 	i_lnode = kmem_zalloc(sizeof (i_lnode_t), KM_SLEEP);
1816*0Sstevel@tonic-gate 
1817*0Sstevel@tonic-gate 	ASSERT(modid != -1);
1818*0Sstevel@tonic-gate 	i_lnode->modid = modid;
1819*0Sstevel@tonic-gate 
1820*0Sstevel@tonic-gate 	return (i_lnode);
1821*0Sstevel@tonic-gate }
1822*0Sstevel@tonic-gate 
1823*0Sstevel@tonic-gate static void
1824*0Sstevel@tonic-gate i_lnode_free(i_lnode_t *i_lnode)
1825*0Sstevel@tonic-gate {
1826*0Sstevel@tonic-gate 	kmem_free(i_lnode, sizeof (i_lnode_t));
1827*0Sstevel@tonic-gate }
1828*0Sstevel@tonic-gate 
1829*0Sstevel@tonic-gate static void
1830*0Sstevel@tonic-gate i_lnode_check_free(i_lnode_t *i_lnode)
1831*0Sstevel@tonic-gate {
1832*0Sstevel@tonic-gate 	/* This lnode and its dip must have been snapshotted */
1833*0Sstevel@tonic-gate 	ASSERT(i_lnode->self > 0);
1834*0Sstevel@tonic-gate 	ASSERT(i_lnode->di_node->self > 0);
1835*0Sstevel@tonic-gate 
1836*0Sstevel@tonic-gate 	/* at least 1 link (in or out) must exist for this lnode */
1837*0Sstevel@tonic-gate 	ASSERT(i_lnode->link_in || i_lnode->link_out);
1838*0Sstevel@tonic-gate 
1839*0Sstevel@tonic-gate 	i_lnode_free(i_lnode);
1840*0Sstevel@tonic-gate }
1841*0Sstevel@tonic-gate 
1842*0Sstevel@tonic-gate static i_link_t *
1843*0Sstevel@tonic-gate i_link_alloc(int spec_type)
1844*0Sstevel@tonic-gate {
1845*0Sstevel@tonic-gate 	i_link_t *i_link;
1846*0Sstevel@tonic-gate 
1847*0Sstevel@tonic-gate 	i_link = kmem_zalloc(sizeof (i_link_t), KM_SLEEP);
1848*0Sstevel@tonic-gate 	i_link->spec_type = spec_type;
1849*0Sstevel@tonic-gate 
1850*0Sstevel@tonic-gate 	return (i_link);
1851*0Sstevel@tonic-gate }
1852*0Sstevel@tonic-gate 
1853*0Sstevel@tonic-gate static void
1854*0Sstevel@tonic-gate i_link_check_free(i_link_t *i_link)
1855*0Sstevel@tonic-gate {
1856*0Sstevel@tonic-gate 	/* This link must have been snapshotted */
1857*0Sstevel@tonic-gate 	ASSERT(i_link->self > 0);
1858*0Sstevel@tonic-gate 
1859*0Sstevel@tonic-gate 	/* Both endpoint lnodes must exist for this link */
1860*0Sstevel@tonic-gate 	ASSERT(i_link->src_lnode);
1861*0Sstevel@tonic-gate 	ASSERT(i_link->tgt_lnode);
1862*0Sstevel@tonic-gate 
1863*0Sstevel@tonic-gate 	kmem_free(i_link, sizeof (i_link_t));
1864*0Sstevel@tonic-gate }
1865*0Sstevel@tonic-gate 
1866*0Sstevel@tonic-gate /*ARGSUSED*/
1867*0Sstevel@tonic-gate static uint_t
1868*0Sstevel@tonic-gate i_lnode_hashfunc(void *arg, mod_hash_key_t key)
1869*0Sstevel@tonic-gate {
1870*0Sstevel@tonic-gate 	i_lnode_t	*i_lnode = (i_lnode_t *)key;
1871*0Sstevel@tonic-gate 	struct di_node	*ptr;
1872*0Sstevel@tonic-gate 	dev_t		dev;
1873*0Sstevel@tonic-gate 
1874*0Sstevel@tonic-gate 	dev = i_lnode->devt;
1875*0Sstevel@tonic-gate 	if (dev != DDI_DEV_T_NONE)
1876*0Sstevel@tonic-gate 		return (i_lnode->modid + getminor(dev) + getmajor(dev));
1877*0Sstevel@tonic-gate 
1878*0Sstevel@tonic-gate 	ptr = i_lnode->di_node;
1879*0Sstevel@tonic-gate 	ASSERT(ptr->self > 0);
1880*0Sstevel@tonic-gate 	if (ptr) {
1881*0Sstevel@tonic-gate 		uintptr_t k = (uintptr_t)ptr;
1882*0Sstevel@tonic-gate 		k >>= (int)highbit(sizeof (struct di_node));
1883*0Sstevel@tonic-gate 		return ((uint_t)k);
1884*0Sstevel@tonic-gate 	}
1885*0Sstevel@tonic-gate 
1886*0Sstevel@tonic-gate 	return (i_lnode->modid);
1887*0Sstevel@tonic-gate }
1888*0Sstevel@tonic-gate 
1889*0Sstevel@tonic-gate static int
1890*0Sstevel@tonic-gate i_lnode_cmp(void *arg1, void *arg2)
1891*0Sstevel@tonic-gate {
1892*0Sstevel@tonic-gate 	i_lnode_t	*i_lnode1 = (i_lnode_t *)arg1;
1893*0Sstevel@tonic-gate 	i_lnode_t	*i_lnode2 = (i_lnode_t *)arg2;
1894*0Sstevel@tonic-gate 
1895*0Sstevel@tonic-gate 	if (i_lnode1->modid != i_lnode2->modid) {
1896*0Sstevel@tonic-gate 		return ((i_lnode1->modid < i_lnode2->modid) ? -1 : 1);
1897*0Sstevel@tonic-gate 	}
1898*0Sstevel@tonic-gate 
1899*0Sstevel@tonic-gate 	if (i_lnode1->di_node != i_lnode2->di_node)
1900*0Sstevel@tonic-gate 		return ((i_lnode1->di_node < i_lnode2->di_node) ? -1 : 1);
1901*0Sstevel@tonic-gate 
1902*0Sstevel@tonic-gate 	if (i_lnode1->devt != i_lnode2->devt)
1903*0Sstevel@tonic-gate 		return ((i_lnode1->devt < i_lnode2->devt) ? -1 : 1);
1904*0Sstevel@tonic-gate 
1905*0Sstevel@tonic-gate 	return (0);
1906*0Sstevel@tonic-gate }
1907*0Sstevel@tonic-gate 
1908*0Sstevel@tonic-gate /*
1909*0Sstevel@tonic-gate  * An lnode represents a {dip, dev_t} tuple. A link represents a
1910*0Sstevel@tonic-gate  * {src_lnode, tgt_lnode, spec_type} tuple.
1911*0Sstevel@tonic-gate  * The following callback assumes that LDI framework ref-counts the
1912*0Sstevel@tonic-gate  * src_dip and tgt_dip while invoking this callback.
1913*0Sstevel@tonic-gate  */
1914*0Sstevel@tonic-gate static int
1915*0Sstevel@tonic-gate di_ldi_callback(const ldi_usage_t *ldi_usage, void *arg)
1916*0Sstevel@tonic-gate {
1917*0Sstevel@tonic-gate 	struct di_state	*st = (struct di_state *)arg;
1918*0Sstevel@tonic-gate 	i_lnode_t	*src_lnode, *tgt_lnode, *i_lnode;
1919*0Sstevel@tonic-gate 	i_link_t	**i_link_next, *i_link;
1920*0Sstevel@tonic-gate 	di_off_t	soff, toff;
1921*0Sstevel@tonic-gate 	mod_hash_val_t	nodep = NULL;
1922*0Sstevel@tonic-gate 	int		res;
1923*0Sstevel@tonic-gate 
1924*0Sstevel@tonic-gate 	/*
1925*0Sstevel@tonic-gate 	 * if the source or target of this device usage information doesn't
1926*0Sstevel@tonic-gate 	 * corrospond to a device node then we don't report it via
1927*0Sstevel@tonic-gate 	 * libdevinfo so return.
1928*0Sstevel@tonic-gate 	 */
1929*0Sstevel@tonic-gate 	if ((ldi_usage->src_dip == NULL) || (ldi_usage->tgt_dip == NULL))
1930*0Sstevel@tonic-gate 		return (LDI_USAGE_CONTINUE);
1931*0Sstevel@tonic-gate 
1932*0Sstevel@tonic-gate 	ASSERT(e_ddi_devi_holdcnt(ldi_usage->src_dip));
1933*0Sstevel@tonic-gate 	ASSERT(e_ddi_devi_holdcnt(ldi_usage->tgt_dip));
1934*0Sstevel@tonic-gate 
1935*0Sstevel@tonic-gate 	/*
1936*0Sstevel@tonic-gate 	 * Skip the ldi_usage if either src or tgt dip is not in the
1937*0Sstevel@tonic-gate 	 * snapshot. This saves us from pruning bad lnodes/links later.
1938*0Sstevel@tonic-gate 	 */
1939*0Sstevel@tonic-gate 	if (di_dip_find(st, ldi_usage->src_dip, &soff) != 0)
1940*0Sstevel@tonic-gate 		return (LDI_USAGE_CONTINUE);
1941*0Sstevel@tonic-gate 	if (di_dip_find(st, ldi_usage->tgt_dip, &toff) != 0)
1942*0Sstevel@tonic-gate 		return (LDI_USAGE_CONTINUE);
1943*0Sstevel@tonic-gate 
1944*0Sstevel@tonic-gate 	ASSERT(soff > 0);
1945*0Sstevel@tonic-gate 	ASSERT(toff > 0);
1946*0Sstevel@tonic-gate 
1947*0Sstevel@tonic-gate 	/*
1948*0Sstevel@tonic-gate 	 * allocate an i_lnode and add it to the lnode hash
1949*0Sstevel@tonic-gate 	 * if it is not already present. For this particular
1950*0Sstevel@tonic-gate 	 * link the lnode is a source, but it may
1951*0Sstevel@tonic-gate 	 * participate as tgt or src in any number of layered
1952*0Sstevel@tonic-gate 	 * operations - so it may already be in the hash.
1953*0Sstevel@tonic-gate 	 */
1954*0Sstevel@tonic-gate 	i_lnode = i_lnode_alloc(ldi_usage->src_modid);
1955*0Sstevel@tonic-gate 	i_lnode->di_node = (struct di_node *)di_mem_addr(st, soff);
1956*0Sstevel@tonic-gate 	i_lnode->devt = ldi_usage->src_devt;
1957*0Sstevel@tonic-gate 
1958*0Sstevel@tonic-gate 	res = mod_hash_find(st->lnode_hash, i_lnode, &nodep);
1959*0Sstevel@tonic-gate 	if (res == MH_ERR_NOTFOUND) {
1960*0Sstevel@tonic-gate 		/*
1961*0Sstevel@tonic-gate 		 * new i_lnode
1962*0Sstevel@tonic-gate 		 * add it to the hash and increment the lnode count
1963*0Sstevel@tonic-gate 		 */
1964*0Sstevel@tonic-gate 		res = mod_hash_insert(st->lnode_hash, i_lnode, i_lnode);
1965*0Sstevel@tonic-gate 		ASSERT(res == 0);
1966*0Sstevel@tonic-gate 		st->lnode_count++;
1967*0Sstevel@tonic-gate 		src_lnode = i_lnode;
1968*0Sstevel@tonic-gate 	} else {
1969*0Sstevel@tonic-gate 		/* this i_lnode already exists in the lnode_hash */
1970*0Sstevel@tonic-gate 		i_lnode_free(i_lnode);
1971*0Sstevel@tonic-gate 		src_lnode = (i_lnode_t *)nodep;
1972*0Sstevel@tonic-gate 	}
1973*0Sstevel@tonic-gate 
1974*0Sstevel@tonic-gate 	/*
1975*0Sstevel@tonic-gate 	 * allocate a tgt i_lnode and add it to the lnode hash
1976*0Sstevel@tonic-gate 	 */
1977*0Sstevel@tonic-gate 	i_lnode = i_lnode_alloc(ldi_usage->tgt_modid);
1978*0Sstevel@tonic-gate 	i_lnode->di_node = (struct di_node *)di_mem_addr(st, toff);
1979*0Sstevel@tonic-gate 	i_lnode->devt = ldi_usage->tgt_devt;
1980*0Sstevel@tonic-gate 
1981*0Sstevel@tonic-gate 	res = mod_hash_find(st->lnode_hash, i_lnode, &nodep);
1982*0Sstevel@tonic-gate 	if (res == MH_ERR_NOTFOUND) {
1983*0Sstevel@tonic-gate 		/*
1984*0Sstevel@tonic-gate 		 * new i_lnode
1985*0Sstevel@tonic-gate 		 * add it to the hash and increment the lnode count
1986*0Sstevel@tonic-gate 		 */
1987*0Sstevel@tonic-gate 		res = mod_hash_insert(st->lnode_hash, i_lnode, i_lnode);
1988*0Sstevel@tonic-gate 		ASSERT(res == 0);
1989*0Sstevel@tonic-gate 		st->lnode_count++;
1990*0Sstevel@tonic-gate 		tgt_lnode = i_lnode;
1991*0Sstevel@tonic-gate 	} else {
1992*0Sstevel@tonic-gate 		/* this i_lnode already exists in the lnode_hash */
1993*0Sstevel@tonic-gate 		i_lnode_free(i_lnode);
1994*0Sstevel@tonic-gate 		tgt_lnode = (i_lnode_t *)nodep;
1995*0Sstevel@tonic-gate 	}
1996*0Sstevel@tonic-gate 
1997*0Sstevel@tonic-gate 	/*
1998*0Sstevel@tonic-gate 	 * allocate a i_link
1999*0Sstevel@tonic-gate 	 */
2000*0Sstevel@tonic-gate 	i_link = i_link_alloc(ldi_usage->tgt_spec_type);
2001*0Sstevel@tonic-gate 	i_link->src_lnode = src_lnode;
2002*0Sstevel@tonic-gate 	i_link->tgt_lnode = tgt_lnode;
2003*0Sstevel@tonic-gate 
2004*0Sstevel@tonic-gate 	/*
2005*0Sstevel@tonic-gate 	 * add this link onto the src i_lnodes outbound i_link list
2006*0Sstevel@tonic-gate 	 */
2007*0Sstevel@tonic-gate 	i_link_next = &(src_lnode->link_out);
2008*0Sstevel@tonic-gate 	while (*i_link_next != NULL) {
2009*0Sstevel@tonic-gate 		if ((i_lnode_cmp(tgt_lnode, (*i_link_next)->tgt_lnode) == 0) &&
2010*0Sstevel@tonic-gate 		    (i_link->spec_type == (*i_link_next)->spec_type)) {
2011*0Sstevel@tonic-gate 			/* this link already exists */
2012*0Sstevel@tonic-gate 			kmem_free(i_link, sizeof (i_link_t));
2013*0Sstevel@tonic-gate 			return (LDI_USAGE_CONTINUE);
2014*0Sstevel@tonic-gate 		}
2015*0Sstevel@tonic-gate 		i_link_next = &((*i_link_next)->src_link_next);
2016*0Sstevel@tonic-gate 	}
2017*0Sstevel@tonic-gate 	*i_link_next = i_link;
2018*0Sstevel@tonic-gate 
2019*0Sstevel@tonic-gate 	/*
2020*0Sstevel@tonic-gate 	 * add this link onto the tgt i_lnodes inbound i_link list
2021*0Sstevel@tonic-gate 	 */
2022*0Sstevel@tonic-gate 	i_link_next = &(tgt_lnode->link_in);
2023*0Sstevel@tonic-gate 	while (*i_link_next != NULL) {
2024*0Sstevel@tonic-gate 		ASSERT(i_lnode_cmp(src_lnode, (*i_link_next)->src_lnode) != 0);
2025*0Sstevel@tonic-gate 		i_link_next = &((*i_link_next)->tgt_link_next);
2026*0Sstevel@tonic-gate 	}
2027*0Sstevel@tonic-gate 	*i_link_next = i_link;
2028*0Sstevel@tonic-gate 
2029*0Sstevel@tonic-gate 	/*
2030*0Sstevel@tonic-gate 	 * add this i_link to the link hash
2031*0Sstevel@tonic-gate 	 */
2032*0Sstevel@tonic-gate 	res = mod_hash_insert(st->link_hash, i_link, i_link);
2033*0Sstevel@tonic-gate 	ASSERT(res == 0);
2034*0Sstevel@tonic-gate 	st->link_count++;
2035*0Sstevel@tonic-gate 
2036*0Sstevel@tonic-gate 	return (LDI_USAGE_CONTINUE);
2037*0Sstevel@tonic-gate }
2038*0Sstevel@tonic-gate 
2039*0Sstevel@tonic-gate struct i_layer_data {
2040*0Sstevel@tonic-gate 	struct di_state	*st;
2041*0Sstevel@tonic-gate 	int		lnode_count;
2042*0Sstevel@tonic-gate 	int		link_count;
2043*0Sstevel@tonic-gate 	di_off_t	lnode_off;
2044*0Sstevel@tonic-gate 	di_off_t 	link_off;
2045*0Sstevel@tonic-gate };
2046*0Sstevel@tonic-gate 
2047*0Sstevel@tonic-gate /*ARGSUSED*/
2048*0Sstevel@tonic-gate static uint_t
2049*0Sstevel@tonic-gate i_link_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
2050*0Sstevel@tonic-gate {
2051*0Sstevel@tonic-gate 	i_link_t		*i_link  = (i_link_t *)key;
2052*0Sstevel@tonic-gate 	struct i_layer_data	*data = arg;
2053*0Sstevel@tonic-gate 	struct di_link		*me;
2054*0Sstevel@tonic-gate 	struct di_lnode		*melnode;
2055*0Sstevel@tonic-gate 	struct di_node		*medinode;
2056*0Sstevel@tonic-gate 
2057*0Sstevel@tonic-gate 	ASSERT(i_link->self == 0);
2058*0Sstevel@tonic-gate 
2059*0Sstevel@tonic-gate 	i_link->self = data->link_off +
2060*0Sstevel@tonic-gate 	    (data->link_count * sizeof (struct di_link));
2061*0Sstevel@tonic-gate 	data->link_count++;
2062*0Sstevel@tonic-gate 
2063*0Sstevel@tonic-gate 	ASSERT(data->link_off > 0 && data->link_count > 0);
2064*0Sstevel@tonic-gate 	ASSERT(data->lnode_count == data->st->lnode_count); /* lnodes done */
2065*0Sstevel@tonic-gate 	ASSERT(data->link_count <= data->st->link_count);
2066*0Sstevel@tonic-gate 
2067*0Sstevel@tonic-gate 	/* fill in fields for the di_link snapshot */
2068*0Sstevel@tonic-gate 	me = (struct di_link *)di_mem_addr(data->st, i_link->self);
2069*0Sstevel@tonic-gate 	me->self = i_link->self;
2070*0Sstevel@tonic-gate 	me->spec_type = i_link->spec_type;
2071*0Sstevel@tonic-gate 
2072*0Sstevel@tonic-gate 	/*
2073*0Sstevel@tonic-gate 	 * The src_lnode and tgt_lnode i_lnode_t for this i_link_t
2074*0Sstevel@tonic-gate 	 * are created during the LDI table walk. Since we are
2075*0Sstevel@tonic-gate 	 * walking the link hash, the lnode hash has already been
2076*0Sstevel@tonic-gate 	 * walked and the lnodes have been snapshotted. Save lnode
2077*0Sstevel@tonic-gate 	 * offsets.
2078*0Sstevel@tonic-gate 	 */
2079*0Sstevel@tonic-gate 	me->src_lnode = i_link->src_lnode->self;
2080*0Sstevel@tonic-gate 	me->tgt_lnode = i_link->tgt_lnode->self;
2081*0Sstevel@tonic-gate 
2082*0Sstevel@tonic-gate 	/*
2083*0Sstevel@tonic-gate 	 * Save this link's offset in the src_lnode snapshot's link_out
2084*0Sstevel@tonic-gate 	 * field
2085*0Sstevel@tonic-gate 	 */
2086*0Sstevel@tonic-gate 	melnode = (struct di_lnode *)di_mem_addr(data->st, me->src_lnode);
2087*0Sstevel@tonic-gate 	me->src_link_next = melnode->link_out;
2088*0Sstevel@tonic-gate 	melnode->link_out = me->self;
2089*0Sstevel@tonic-gate 
2090*0Sstevel@tonic-gate 	/*
2091*0Sstevel@tonic-gate 	 * Put this link on the tgt_lnode's link_in field
2092*0Sstevel@tonic-gate 	 */
2093*0Sstevel@tonic-gate 	melnode = (struct di_lnode *)di_mem_addr(data->st, me->tgt_lnode);
2094*0Sstevel@tonic-gate 	me->tgt_link_next = melnode->link_in;
2095*0Sstevel@tonic-gate 	melnode->link_in = me->self;
2096*0Sstevel@tonic-gate 
2097*0Sstevel@tonic-gate 	/*
2098*0Sstevel@tonic-gate 	 * An i_lnode_t is only created if the corresponding dip exists
2099*0Sstevel@tonic-gate 	 * in the snapshot. A pointer to the di_node is saved in the
2100*0Sstevel@tonic-gate 	 * i_lnode_t when it is allocated. For this link, get the di_node
2101*0Sstevel@tonic-gate 	 * for the source lnode. Then put the link on the di_node's list
2102*0Sstevel@tonic-gate 	 * of src links
2103*0Sstevel@tonic-gate 	 */
2104*0Sstevel@tonic-gate 	medinode = i_link->src_lnode->di_node;
2105*0Sstevel@tonic-gate 	me->src_node_next = medinode->src_links;
2106*0Sstevel@tonic-gate 	medinode->src_links = me->self;
2107*0Sstevel@tonic-gate 
2108*0Sstevel@tonic-gate 	/*
2109*0Sstevel@tonic-gate 	 * Put this link on the tgt_links list of the target
2110*0Sstevel@tonic-gate 	 * dip.
2111*0Sstevel@tonic-gate 	 */
2112*0Sstevel@tonic-gate 	medinode = i_link->tgt_lnode->di_node;
2113*0Sstevel@tonic-gate 	me->tgt_node_next = medinode->tgt_links;
2114*0Sstevel@tonic-gate 	medinode->tgt_links = me->self;
2115*0Sstevel@tonic-gate 
2116*0Sstevel@tonic-gate 	return (MH_WALK_CONTINUE);
2117*0Sstevel@tonic-gate }
2118*0Sstevel@tonic-gate 
2119*0Sstevel@tonic-gate /*ARGSUSED*/
2120*0Sstevel@tonic-gate static uint_t
2121*0Sstevel@tonic-gate i_lnode_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
2122*0Sstevel@tonic-gate {
2123*0Sstevel@tonic-gate 	i_lnode_t		*i_lnode = (i_lnode_t *)key;
2124*0Sstevel@tonic-gate 	struct i_layer_data	*data = arg;
2125*0Sstevel@tonic-gate 	struct di_lnode		*me;
2126*0Sstevel@tonic-gate 	struct di_node		*medinode;
2127*0Sstevel@tonic-gate 
2128*0Sstevel@tonic-gate 	ASSERT(i_lnode->self == 0);
2129*0Sstevel@tonic-gate 
2130*0Sstevel@tonic-gate 	i_lnode->self = data->lnode_off +
2131*0Sstevel@tonic-gate 	    (data->lnode_count * sizeof (struct di_lnode));
2132*0Sstevel@tonic-gate 	data->lnode_count++;
2133*0Sstevel@tonic-gate 
2134*0Sstevel@tonic-gate 	ASSERT(data->lnode_off > 0 && data->lnode_count > 0);
2135*0Sstevel@tonic-gate 	ASSERT(data->link_count == 0); /* links not done yet */
2136*0Sstevel@tonic-gate 	ASSERT(data->lnode_count <= data->st->lnode_count);
2137*0Sstevel@tonic-gate 
2138*0Sstevel@tonic-gate 	/* fill in fields for the di_lnode snapshot */
2139*0Sstevel@tonic-gate 	me = (struct di_lnode *)di_mem_addr(data->st, i_lnode->self);
2140*0Sstevel@tonic-gate 	me->self = i_lnode->self;
2141*0Sstevel@tonic-gate 
2142*0Sstevel@tonic-gate 	if (i_lnode->devt == DDI_DEV_T_NONE) {
2143*0Sstevel@tonic-gate 		me->dev_major = (major_t)-1;
2144*0Sstevel@tonic-gate 		me->dev_minor = (minor_t)-1;
2145*0Sstevel@tonic-gate 	} else {
2146*0Sstevel@tonic-gate 		me->dev_major = getmajor(i_lnode->devt);
2147*0Sstevel@tonic-gate 		me->dev_minor = getminor(i_lnode->devt);
2148*0Sstevel@tonic-gate 	}
2149*0Sstevel@tonic-gate 
2150*0Sstevel@tonic-gate 	/*
2151*0Sstevel@tonic-gate 	 * The dip corresponding to this lnode must exist in
2152*0Sstevel@tonic-gate 	 * the snapshot or we wouldn't have created the i_lnode_t
2153*0Sstevel@tonic-gate 	 * during LDI walk. Save the offset of the dip.
2154*0Sstevel@tonic-gate 	 */
2155*0Sstevel@tonic-gate 	ASSERT(i_lnode->di_node && i_lnode->di_node->self > 0);
2156*0Sstevel@tonic-gate 	me->node = i_lnode->di_node->self;
2157*0Sstevel@tonic-gate 
2158*0Sstevel@tonic-gate 	/*
2159*0Sstevel@tonic-gate 	 * There must be at least one link in or out of this lnode
2160*0Sstevel@tonic-gate 	 * or we wouldn't have created it. These fields will be set
2161*0Sstevel@tonic-gate 	 * during the link hash walk.
2162*0Sstevel@tonic-gate 	 */
2163*0Sstevel@tonic-gate 	ASSERT((i_lnode->link_in != NULL) || (i_lnode->link_out != NULL));
2164*0Sstevel@tonic-gate 
2165*0Sstevel@tonic-gate 	/*
2166*0Sstevel@tonic-gate 	 * set the offset of the devinfo node associated with this
2167*0Sstevel@tonic-gate 	 * lnode. Also update the node_next next pointer.  this pointer
2168*0Sstevel@tonic-gate 	 * is set if there are multiple lnodes associated with the same
2169*0Sstevel@tonic-gate 	 * devinfo node.  (could occure when multiple minor nodes
2170*0Sstevel@tonic-gate 	 * are open for one device, etc.)
2171*0Sstevel@tonic-gate 	 */
2172*0Sstevel@tonic-gate 	medinode = i_lnode->di_node;
2173*0Sstevel@tonic-gate 	me->node_next = medinode->lnodes;
2174*0Sstevel@tonic-gate 	medinode->lnodes = me->self;
2175*0Sstevel@tonic-gate 
2176*0Sstevel@tonic-gate 	return (MH_WALK_CONTINUE);
2177*0Sstevel@tonic-gate }
2178*0Sstevel@tonic-gate 
2179*0Sstevel@tonic-gate static di_off_t
2180*0Sstevel@tonic-gate di_getlink_data(di_off_t off, struct di_state *st)
2181*0Sstevel@tonic-gate {
2182*0Sstevel@tonic-gate 	struct i_layer_data data = {0};
2183*0Sstevel@tonic-gate 	size_t size;
2184*0Sstevel@tonic-gate 
2185*0Sstevel@tonic-gate 	dcmn_err2((CE_CONT, "di_copylyr: off = %x\n", off));
2186*0Sstevel@tonic-gate 
2187*0Sstevel@tonic-gate 	st->lnode_hash = mod_hash_create_extended("di_lnode_hash", 32,
2188*0Sstevel@tonic-gate 	    mod_hash_null_keydtor, (void (*)(mod_hash_val_t))i_lnode_check_free,
2189*0Sstevel@tonic-gate 	    i_lnode_hashfunc, NULL, i_lnode_cmp, KM_SLEEP);
2190*0Sstevel@tonic-gate 
2191*0Sstevel@tonic-gate 	st->link_hash = mod_hash_create_ptrhash("di_link_hash", 32,
2192*0Sstevel@tonic-gate 	    (void (*)(mod_hash_val_t))i_link_check_free, sizeof (i_link_t));
2193*0Sstevel@tonic-gate 
2194*0Sstevel@tonic-gate 	/* get driver layering information */
2195*0Sstevel@tonic-gate 	(void) ldi_usage_walker(st, di_ldi_callback);
2196*0Sstevel@tonic-gate 
2197*0Sstevel@tonic-gate 	/* check if there is any link data to include in the snapshot */
2198*0Sstevel@tonic-gate 	if (st->lnode_count == 0) {
2199*0Sstevel@tonic-gate 		ASSERT(st->link_count == 0);
2200*0Sstevel@tonic-gate 		goto out;
2201*0Sstevel@tonic-gate 	}
2202*0Sstevel@tonic-gate 
2203*0Sstevel@tonic-gate 	ASSERT(st->link_count != 0);
2204*0Sstevel@tonic-gate 
2205*0Sstevel@tonic-gate 	/* get a pointer to snapshot memory for all the di_lnodes */
2206*0Sstevel@tonic-gate 	size = sizeof (struct di_lnode) * st->lnode_count;
2207*0Sstevel@tonic-gate 	data.lnode_off = off = di_checkmem(st, off, size);
2208*0Sstevel@tonic-gate 	off += DI_ALIGN(size);
2209*0Sstevel@tonic-gate 
2210*0Sstevel@tonic-gate 	/* get a pointer to snapshot memory for all the di_links */
2211*0Sstevel@tonic-gate 	size = sizeof (struct di_link) * st->link_count;
2212*0Sstevel@tonic-gate 	data.link_off = off = di_checkmem(st, off, size);
2213*0Sstevel@tonic-gate 	off += DI_ALIGN(size);
2214*0Sstevel@tonic-gate 
2215*0Sstevel@tonic-gate 	data.lnode_count = data.link_count = 0;
2216*0Sstevel@tonic-gate 	data.st = st;
2217*0Sstevel@tonic-gate 
2218*0Sstevel@tonic-gate 	/*
2219*0Sstevel@tonic-gate 	 * We have lnodes and links that will go into the
2220*0Sstevel@tonic-gate 	 * snapshot, so let's walk the respective hashes
2221*0Sstevel@tonic-gate 	 * and snapshot them. The various linkages are
2222*0Sstevel@tonic-gate 	 * also set up during the walk.
2223*0Sstevel@tonic-gate 	 */
2224*0Sstevel@tonic-gate 	mod_hash_walk(st->lnode_hash, i_lnode_walker, (void *)&data);
2225*0Sstevel@tonic-gate 	ASSERT(data.lnode_count == st->lnode_count);
2226*0Sstevel@tonic-gate 
2227*0Sstevel@tonic-gate 	mod_hash_walk(st->link_hash, i_link_walker, (void *)&data);
2228*0Sstevel@tonic-gate 	ASSERT(data.link_count == st->link_count);
2229*0Sstevel@tonic-gate 
2230*0Sstevel@tonic-gate out:
2231*0Sstevel@tonic-gate 	/* free up the i_lnodes and i_links used to create the snapshot */
2232*0Sstevel@tonic-gate 	mod_hash_destroy_hash(st->lnode_hash);
2233*0Sstevel@tonic-gate 	mod_hash_destroy_hash(st->link_hash);
2234*0Sstevel@tonic-gate 	st->lnode_count = 0;
2235*0Sstevel@tonic-gate 	st->link_count = 0;
2236*0Sstevel@tonic-gate 
2237*0Sstevel@tonic-gate 	return (off);
2238*0Sstevel@tonic-gate }
2239*0Sstevel@tonic-gate 
2240*0Sstevel@tonic-gate 
2241*0Sstevel@tonic-gate /*
2242*0Sstevel@tonic-gate  * Copy all minor data nodes attached to a devinfo node into the snapshot.
2243*0Sstevel@tonic-gate  * It is called from di_copynode with devi_lock held.
2244*0Sstevel@tonic-gate  */
2245*0Sstevel@tonic-gate static di_off_t
2246*0Sstevel@tonic-gate di_getmdata(struct ddi_minor_data *mnode, di_off_t *off_p, di_off_t node,
2247*0Sstevel@tonic-gate 	struct di_state *st)
2248*0Sstevel@tonic-gate {
2249*0Sstevel@tonic-gate 	di_off_t off;
2250*0Sstevel@tonic-gate 	struct di_minor *me;
2251*0Sstevel@tonic-gate 
2252*0Sstevel@tonic-gate 	dcmn_err2((CE_CONT, "di_getmdata:\n"));
2253*0Sstevel@tonic-gate 
2254*0Sstevel@tonic-gate 	/*
2255*0Sstevel@tonic-gate 	 * check memory first
2256*0Sstevel@tonic-gate 	 */
2257*0Sstevel@tonic-gate 	off = di_checkmem(st, *off_p, sizeof (struct di_minor));
2258*0Sstevel@tonic-gate 	*off_p = off;
2259*0Sstevel@tonic-gate 
2260*0Sstevel@tonic-gate 	do {
2261*0Sstevel@tonic-gate 		me = (struct di_minor *)di_mem_addr(st, off);
2262*0Sstevel@tonic-gate 		me->self = off;
2263*0Sstevel@tonic-gate 		me->type = mnode->type;
2264*0Sstevel@tonic-gate 		me->node = node;
2265*0Sstevel@tonic-gate 		me->user_private_data = NULL;
2266*0Sstevel@tonic-gate 
2267*0Sstevel@tonic-gate 		off += DI_ALIGN(sizeof (struct di_minor));
2268*0Sstevel@tonic-gate 
2269*0Sstevel@tonic-gate 		/*
2270*0Sstevel@tonic-gate 		 * Split dev_t to major/minor, so it works for
2271*0Sstevel@tonic-gate 		 * both ILP32 and LP64 model
2272*0Sstevel@tonic-gate 		 */
2273*0Sstevel@tonic-gate 		me->dev_major = getmajor(mnode->ddm_dev);
2274*0Sstevel@tonic-gate 		me->dev_minor = getminor(mnode->ddm_dev);
2275*0Sstevel@tonic-gate 		me->spec_type = mnode->ddm_spec_type;
2276*0Sstevel@tonic-gate 
2277*0Sstevel@tonic-gate 		if (mnode->ddm_name) {
2278*0Sstevel@tonic-gate 			off = di_checkmem(st, off,
2279*0Sstevel@tonic-gate 				strlen(mnode->ddm_name) + 1);
2280*0Sstevel@tonic-gate 			me->name = off;
2281*0Sstevel@tonic-gate 			(void) strcpy(di_mem_addr(st, off), mnode->ddm_name);
2282*0Sstevel@tonic-gate 			off += DI_ALIGN(strlen(mnode->ddm_name) + 1);
2283*0Sstevel@tonic-gate 		}
2284*0Sstevel@tonic-gate 
2285*0Sstevel@tonic-gate 		if (mnode->ddm_node_type) {
2286*0Sstevel@tonic-gate 			off = di_checkmem(st, off,
2287*0Sstevel@tonic-gate 				strlen(mnode->ddm_node_type) + 1);
2288*0Sstevel@tonic-gate 			me->node_type = off;
2289*0Sstevel@tonic-gate 			(void) strcpy(di_mem_addr(st, off),
2290*0Sstevel@tonic-gate 					mnode->ddm_node_type);
2291*0Sstevel@tonic-gate 			off += DI_ALIGN(strlen(mnode->ddm_node_type) + 1);
2292*0Sstevel@tonic-gate 		}
2293*0Sstevel@tonic-gate 
2294*0Sstevel@tonic-gate 		off = di_checkmem(st, off, sizeof (struct di_minor));
2295*0Sstevel@tonic-gate 		me->next = off;
2296*0Sstevel@tonic-gate 		mnode = mnode->next;
2297*0Sstevel@tonic-gate 	} while (mnode);
2298*0Sstevel@tonic-gate 
2299*0Sstevel@tonic-gate 	me->next = 0;
2300*0Sstevel@tonic-gate 
2301*0Sstevel@tonic-gate 	return (off);
2302*0Sstevel@tonic-gate }
2303*0Sstevel@tonic-gate 
2304*0Sstevel@tonic-gate /*
2305*0Sstevel@tonic-gate  * di_register_dip(), di_find_dip(): The dip must be protected
2306*0Sstevel@tonic-gate  * from deallocation when using these routines - this can either
2307*0Sstevel@tonic-gate  * be a reference count, a busy hold or a per-driver lock.
2308*0Sstevel@tonic-gate  */
2309*0Sstevel@tonic-gate 
2310*0Sstevel@tonic-gate static void
2311*0Sstevel@tonic-gate di_register_dip(struct di_state *st, dev_info_t *dip, di_off_t off)
2312*0Sstevel@tonic-gate {
2313*0Sstevel@tonic-gate 	struct dev_info *node = DEVI(dip);
2314*0Sstevel@tonic-gate 	struct di_key *key = kmem_zalloc(sizeof (*key), KM_SLEEP);
2315*0Sstevel@tonic-gate 	struct di_dkey *dk;
2316*0Sstevel@tonic-gate 
2317*0Sstevel@tonic-gate 	ASSERT(dip);
2318*0Sstevel@tonic-gate 	ASSERT(off > 0);
2319*0Sstevel@tonic-gate 
2320*0Sstevel@tonic-gate 	key->k_type = DI_DKEY;
2321*0Sstevel@tonic-gate 	dk = &(key->k_u.dkey);
2322*0Sstevel@tonic-gate 
2323*0Sstevel@tonic-gate 	dk->dk_dip = dip;
2324*0Sstevel@tonic-gate 	dk->dk_major = node->devi_major;
2325*0Sstevel@tonic-gate 	dk->dk_inst = node->devi_instance;
2326*0Sstevel@tonic-gate 	dk->dk_nodeid = node->devi_nodeid;
2327*0Sstevel@tonic-gate 
2328*0Sstevel@tonic-gate 	if (mod_hash_insert(st->reg_dip_hash, (mod_hash_key_t)key,
2329*0Sstevel@tonic-gate 	    (mod_hash_val_t)(uintptr_t)off) != 0) {
2330*0Sstevel@tonic-gate 		panic(
2331*0Sstevel@tonic-gate 		    "duplicate devinfo (%p) registered during device "
2332*0Sstevel@tonic-gate 		    "tree walk", (void *)dip);
2333*0Sstevel@tonic-gate 	}
2334*0Sstevel@tonic-gate }
2335*0Sstevel@tonic-gate 
2336*0Sstevel@tonic-gate 
2337*0Sstevel@tonic-gate static int
2338*0Sstevel@tonic-gate di_dip_find(struct di_state *st, dev_info_t *dip, di_off_t *off_p)
2339*0Sstevel@tonic-gate {
2340*0Sstevel@tonic-gate 	/*
2341*0Sstevel@tonic-gate 	 * uintptr_t must be used because it matches the size of void *;
2342*0Sstevel@tonic-gate 	 * mod_hash expects clients to place results into pointer-size
2343*0Sstevel@tonic-gate 	 * containers; since di_off_t is always a 32-bit offset, alignment
2344*0Sstevel@tonic-gate 	 * would otherwise be broken on 64-bit kernels.
2345*0Sstevel@tonic-gate 	 */
2346*0Sstevel@tonic-gate 	uintptr_t	offset;
2347*0Sstevel@tonic-gate 	struct		di_key key = {0};
2348*0Sstevel@tonic-gate 	struct		di_dkey *dk;
2349*0Sstevel@tonic-gate 
2350*0Sstevel@tonic-gate 	ASSERT(st->reg_dip_hash);
2351*0Sstevel@tonic-gate 	ASSERT(dip);
2352*0Sstevel@tonic-gate 	ASSERT(off_p);
2353*0Sstevel@tonic-gate 
2354*0Sstevel@tonic-gate 
2355*0Sstevel@tonic-gate 	key.k_type = DI_DKEY;
2356*0Sstevel@tonic-gate 	dk = &(key.k_u.dkey);
2357*0Sstevel@tonic-gate 
2358*0Sstevel@tonic-gate 	dk->dk_dip = dip;
2359*0Sstevel@tonic-gate 	dk->dk_major = DEVI(dip)->devi_major;
2360*0Sstevel@tonic-gate 	dk->dk_inst = DEVI(dip)->devi_instance;
2361*0Sstevel@tonic-gate 	dk->dk_nodeid = DEVI(dip)->devi_nodeid;
2362*0Sstevel@tonic-gate 
2363*0Sstevel@tonic-gate 	if (mod_hash_find(st->reg_dip_hash, (mod_hash_key_t)&key,
2364*0Sstevel@tonic-gate 	    (mod_hash_val_t *)&offset) == 0) {
2365*0Sstevel@tonic-gate 		*off_p = (di_off_t)offset;
2366*0Sstevel@tonic-gate 		return (0);
2367*0Sstevel@tonic-gate 	} else {
2368*0Sstevel@tonic-gate 		return (-1);
2369*0Sstevel@tonic-gate 	}
2370*0Sstevel@tonic-gate }
2371*0Sstevel@tonic-gate 
2372*0Sstevel@tonic-gate /*
2373*0Sstevel@tonic-gate  * di_register_pip(), di_find_pip(): The pip must be protected from deallocation
2374*0Sstevel@tonic-gate  * when using these routines. The caller must do this by protecting the
2375*0Sstevel@tonic-gate  * client(or phci)<->pip linkage while traversing the list and then holding the
2376*0Sstevel@tonic-gate  * pip when it is found in the list.
2377*0Sstevel@tonic-gate  */
2378*0Sstevel@tonic-gate 
2379*0Sstevel@tonic-gate static void
2380*0Sstevel@tonic-gate di_register_pip(struct di_state *st, mdi_pathinfo_t *pip, di_off_t off)
2381*0Sstevel@tonic-gate {
2382*0Sstevel@tonic-gate 	struct di_key	*key = kmem_zalloc(sizeof (*key), KM_SLEEP);
2383*0Sstevel@tonic-gate 	char		*path_addr;
2384*0Sstevel@tonic-gate 	struct di_pkey	*pk;
2385*0Sstevel@tonic-gate 
2386*0Sstevel@tonic-gate 	ASSERT(pip);
2387*0Sstevel@tonic-gate 	ASSERT(off > 0);
2388*0Sstevel@tonic-gate 
2389*0Sstevel@tonic-gate 	key->k_type = DI_PKEY;
2390*0Sstevel@tonic-gate 	pk = &(key->k_u.pkey);
2391*0Sstevel@tonic-gate 
2392*0Sstevel@tonic-gate 	pk->pk_pip = pip;
2393*0Sstevel@tonic-gate 	path_addr = mdi_pi_get_addr(pip);
2394*0Sstevel@tonic-gate 	if (path_addr)
2395*0Sstevel@tonic-gate 		pk->pk_path_addr = i_ddi_strdup(path_addr, KM_SLEEP);
2396*0Sstevel@tonic-gate 	pk->pk_client = mdi_pi_get_client(pip);
2397*0Sstevel@tonic-gate 	pk->pk_phci = mdi_pi_get_phci(pip);
2398*0Sstevel@tonic-gate 
2399*0Sstevel@tonic-gate 	if (mod_hash_insert(st->reg_pip_hash, (mod_hash_key_t)key,
2400*0Sstevel@tonic-gate 	    (mod_hash_val_t)(uintptr_t)off) != 0) {
2401*0Sstevel@tonic-gate 		panic(
2402*0Sstevel@tonic-gate 		    "duplicate pathinfo (%p) registered during device "
2403*0Sstevel@tonic-gate 		    "tree walk", (void *)pip);
2404*0Sstevel@tonic-gate 	}
2405*0Sstevel@tonic-gate }
2406*0Sstevel@tonic-gate 
2407*0Sstevel@tonic-gate /*
2408*0Sstevel@tonic-gate  * As with di_register_pip, the caller must hold or lock the pip
2409*0Sstevel@tonic-gate  */
2410*0Sstevel@tonic-gate static int
2411*0Sstevel@tonic-gate di_pip_find(struct di_state *st, mdi_pathinfo_t *pip, di_off_t *off_p)
2412*0Sstevel@tonic-gate {
2413*0Sstevel@tonic-gate 	/*
2414*0Sstevel@tonic-gate 	 * uintptr_t must be used because it matches the size of void *;
2415*0Sstevel@tonic-gate 	 * mod_hash expects clients to place results into pointer-size
2416*0Sstevel@tonic-gate 	 * containers; since di_off_t is always a 32-bit offset, alignment
2417*0Sstevel@tonic-gate 	 * would otherwise be broken on 64-bit kernels.
2418*0Sstevel@tonic-gate 	 */
2419*0Sstevel@tonic-gate 	uintptr_t	offset;
2420*0Sstevel@tonic-gate 	struct di_key	key = {0};
2421*0Sstevel@tonic-gate 	struct di_pkey	*pk;
2422*0Sstevel@tonic-gate 
2423*0Sstevel@tonic-gate 	ASSERT(st->reg_pip_hash);
2424*0Sstevel@tonic-gate 	ASSERT(off_p);
2425*0Sstevel@tonic-gate 
2426*0Sstevel@tonic-gate 	if (pip == NULL) {
2427*0Sstevel@tonic-gate 		*off_p = 0;
2428*0Sstevel@tonic-gate 		return (0);
2429*0Sstevel@tonic-gate 	}
2430*0Sstevel@tonic-gate 
2431*0Sstevel@tonic-gate 	key.k_type = DI_PKEY;
2432*0Sstevel@tonic-gate 	pk = &(key.k_u.pkey);
2433*0Sstevel@tonic-gate 
2434*0Sstevel@tonic-gate 	pk->pk_pip = pip;
2435*0Sstevel@tonic-gate 	pk->pk_path_addr = mdi_pi_get_addr(pip);
2436*0Sstevel@tonic-gate 	pk->pk_client = mdi_pi_get_client(pip);
2437*0Sstevel@tonic-gate 	pk->pk_phci = mdi_pi_get_phci(pip);
2438*0Sstevel@tonic-gate 
2439*0Sstevel@tonic-gate 	if (mod_hash_find(st->reg_pip_hash, (mod_hash_key_t)&key,
2440*0Sstevel@tonic-gate 	    (mod_hash_val_t *)&offset) == 0) {
2441*0Sstevel@tonic-gate 		*off_p = (di_off_t)offset;
2442*0Sstevel@tonic-gate 		return (0);
2443*0Sstevel@tonic-gate 	} else {
2444*0Sstevel@tonic-gate 		return (-1);
2445*0Sstevel@tonic-gate 	}
2446*0Sstevel@tonic-gate }
2447*0Sstevel@tonic-gate 
2448*0Sstevel@tonic-gate static di_path_state_t
2449*0Sstevel@tonic-gate path_state_convert(mdi_pathinfo_state_t st)
2450*0Sstevel@tonic-gate {
2451*0Sstevel@tonic-gate 	switch (st) {
2452*0Sstevel@tonic-gate 	case MDI_PATHINFO_STATE_ONLINE:
2453*0Sstevel@tonic-gate 		return (DI_PATH_STATE_ONLINE);
2454*0Sstevel@tonic-gate 	case MDI_PATHINFO_STATE_STANDBY:
2455*0Sstevel@tonic-gate 		return (DI_PATH_STATE_STANDBY);
2456*0Sstevel@tonic-gate 	case MDI_PATHINFO_STATE_OFFLINE:
2457*0Sstevel@tonic-gate 		return (DI_PATH_STATE_OFFLINE);
2458*0Sstevel@tonic-gate 	case MDI_PATHINFO_STATE_FAULT:
2459*0Sstevel@tonic-gate 		return (DI_PATH_STATE_FAULT);
2460*0Sstevel@tonic-gate 	default:
2461*0Sstevel@tonic-gate 		return (DI_PATH_STATE_UNKNOWN);
2462*0Sstevel@tonic-gate 	}
2463*0Sstevel@tonic-gate }
2464*0Sstevel@tonic-gate 
2465*0Sstevel@tonic-gate 
2466*0Sstevel@tonic-gate static di_off_t
2467*0Sstevel@tonic-gate di_path_getprop(mdi_pathinfo_t *pip, di_off_t off, di_off_t *off_p,
2468*0Sstevel@tonic-gate     struct di_state *st)
2469*0Sstevel@tonic-gate {
2470*0Sstevel@tonic-gate 	nvpair_t *prop = NULL;
2471*0Sstevel@tonic-gate 	struct di_path_prop *me;
2472*0Sstevel@tonic-gate 
2473*0Sstevel@tonic-gate 	if (mdi_pi_get_next_prop(pip, NULL) == NULL) {
2474*0Sstevel@tonic-gate 		*off_p = 0;
2475*0Sstevel@tonic-gate 		return (off);
2476*0Sstevel@tonic-gate 	}
2477*0Sstevel@tonic-gate 
2478*0Sstevel@tonic-gate 	off = di_checkmem(st, off, sizeof (struct di_path_prop));
2479*0Sstevel@tonic-gate 	*off_p = off;
2480*0Sstevel@tonic-gate 
2481*0Sstevel@tonic-gate 	while (prop = mdi_pi_get_next_prop(pip, prop)) {
2482*0Sstevel@tonic-gate 		int delta = 0;
2483*0Sstevel@tonic-gate 
2484*0Sstevel@tonic-gate 		me = (struct di_path_prop *)di_mem_addr(st, off);
2485*0Sstevel@tonic-gate 		me->self = off;
2486*0Sstevel@tonic-gate 		off += sizeof (struct di_path_prop);
2487*0Sstevel@tonic-gate 
2488*0Sstevel@tonic-gate 		/*
2489*0Sstevel@tonic-gate 		 * property name
2490*0Sstevel@tonic-gate 		 */
2491*0Sstevel@tonic-gate 		off = di_checkmem(st, off, strlen(nvpair_name(prop)) + 1);
2492*0Sstevel@tonic-gate 		me->prop_name = off;
2493*0Sstevel@tonic-gate 		(void) strcpy(di_mem_addr(st, off), nvpair_name(prop));
2494*0Sstevel@tonic-gate 		off += strlen(nvpair_name(prop)) + 1;
2495*0Sstevel@tonic-gate 
2496*0Sstevel@tonic-gate 		switch (nvpair_type(prop)) {
2497*0Sstevel@tonic-gate 		case DATA_TYPE_BYTE:
2498*0Sstevel@tonic-gate 		case DATA_TYPE_INT16:
2499*0Sstevel@tonic-gate 		case DATA_TYPE_UINT16:
2500*0Sstevel@tonic-gate 		case DATA_TYPE_INT32:
2501*0Sstevel@tonic-gate 		case DATA_TYPE_UINT32:
2502*0Sstevel@tonic-gate 			delta = sizeof (int32_t);
2503*0Sstevel@tonic-gate 			me->prop_type = DDI_PROP_TYPE_INT;
2504*0Sstevel@tonic-gate 			off = di_checkmem(st, off, delta);
2505*0Sstevel@tonic-gate 			(void) nvpair_value_int32(prop,
2506*0Sstevel@tonic-gate 			    (int32_t *)di_mem_addr(st, off));
2507*0Sstevel@tonic-gate 			break;
2508*0Sstevel@tonic-gate 
2509*0Sstevel@tonic-gate 		case DATA_TYPE_INT64:
2510*0Sstevel@tonic-gate 		case DATA_TYPE_UINT64:
2511*0Sstevel@tonic-gate 			delta = sizeof (int64_t);
2512*0Sstevel@tonic-gate 			me->prop_type = DDI_PROP_TYPE_INT64;
2513*0Sstevel@tonic-gate 			off = di_checkmem(st, off, delta);
2514*0Sstevel@tonic-gate 			(void) nvpair_value_int64(prop,
2515*0Sstevel@tonic-gate 			    (int64_t *)di_mem_addr(st, off));
2516*0Sstevel@tonic-gate 			break;
2517*0Sstevel@tonic-gate 
2518*0Sstevel@tonic-gate 		case DATA_TYPE_STRING:
2519*0Sstevel@tonic-gate 		{
2520*0Sstevel@tonic-gate 			char *str;
2521*0Sstevel@tonic-gate 			(void) nvpair_value_string(prop, &str);
2522*0Sstevel@tonic-gate 			delta = strlen(str) + 1;
2523*0Sstevel@tonic-gate 			me->prop_type = DDI_PROP_TYPE_STRING;
2524*0Sstevel@tonic-gate 			off = di_checkmem(st, off, delta);
2525*0Sstevel@tonic-gate 			(void) strcpy(di_mem_addr(st, off), str);
2526*0Sstevel@tonic-gate 			break;
2527*0Sstevel@tonic-gate 		}
2528*0Sstevel@tonic-gate 		case DATA_TYPE_BYTE_ARRAY:
2529*0Sstevel@tonic-gate 		case DATA_TYPE_INT16_ARRAY:
2530*0Sstevel@tonic-gate 		case DATA_TYPE_UINT16_ARRAY:
2531*0Sstevel@tonic-gate 		case DATA_TYPE_INT32_ARRAY:
2532*0Sstevel@tonic-gate 		case DATA_TYPE_UINT32_ARRAY:
2533*0Sstevel@tonic-gate 		case DATA_TYPE_INT64_ARRAY:
2534*0Sstevel@tonic-gate 		case DATA_TYPE_UINT64_ARRAY:
2535*0Sstevel@tonic-gate 		{
2536*0Sstevel@tonic-gate 			uchar_t *buf;
2537*0Sstevel@tonic-gate 			uint_t nelems;
2538*0Sstevel@tonic-gate 			(void) nvpair_value_byte_array(prop, &buf, &nelems);
2539*0Sstevel@tonic-gate 			delta = nelems;
2540*0Sstevel@tonic-gate 			me->prop_type = DDI_PROP_TYPE_BYTE;
2541*0Sstevel@tonic-gate 			if (nelems != 0) {
2542*0Sstevel@tonic-gate 				off = di_checkmem(st, off, delta);
2543*0Sstevel@tonic-gate 				bcopy(buf, di_mem_addr(st, off), nelems);
2544*0Sstevel@tonic-gate 			}
2545*0Sstevel@tonic-gate 			break;
2546*0Sstevel@tonic-gate 		}
2547*0Sstevel@tonic-gate 
2548*0Sstevel@tonic-gate 		default:	/* Unknown or unhandled type; skip it */
2549*0Sstevel@tonic-gate 			delta = 0;
2550*0Sstevel@tonic-gate 			break;
2551*0Sstevel@tonic-gate 		}
2552*0Sstevel@tonic-gate 
2553*0Sstevel@tonic-gate 		if (delta > 0) {
2554*0Sstevel@tonic-gate 			me->prop_data = off;
2555*0Sstevel@tonic-gate 		}
2556*0Sstevel@tonic-gate 
2557*0Sstevel@tonic-gate 		me->prop_len = delta;
2558*0Sstevel@tonic-gate 		off += delta;
2559*0Sstevel@tonic-gate 
2560*0Sstevel@tonic-gate 		off = di_checkmem(st, off, sizeof (struct di_path_prop));
2561*0Sstevel@tonic-gate 		me->prop_next = off;
2562*0Sstevel@tonic-gate 	}
2563*0Sstevel@tonic-gate 
2564*0Sstevel@tonic-gate 	me->prop_next = 0;
2565*0Sstevel@tonic-gate 	return (off);
2566*0Sstevel@tonic-gate }
2567*0Sstevel@tonic-gate 
2568*0Sstevel@tonic-gate 
2569*0Sstevel@tonic-gate static void
2570*0Sstevel@tonic-gate di_path_one_endpoint(struct di_path *me, di_off_t noff, di_off_t **off_pp,
2571*0Sstevel@tonic-gate     int get_client)
2572*0Sstevel@tonic-gate {
2573*0Sstevel@tonic-gate 	if (get_client) {
2574*0Sstevel@tonic-gate 		ASSERT(me->path_client == 0);
2575*0Sstevel@tonic-gate 		me->path_client = noff;
2576*0Sstevel@tonic-gate 		ASSERT(me->path_c_link == 0);
2577*0Sstevel@tonic-gate 		*off_pp = &me->path_c_link;
2578*0Sstevel@tonic-gate 		me->path_snap_state &=
2579*0Sstevel@tonic-gate 		    ~(DI_PATH_SNAP_NOCLIENT | DI_PATH_SNAP_NOCLINK);
2580*0Sstevel@tonic-gate 	} else {
2581*0Sstevel@tonic-gate 		ASSERT(me->path_phci == 0);
2582*0Sstevel@tonic-gate 		me->path_phci = noff;
2583*0Sstevel@tonic-gate 		ASSERT(me->path_p_link == 0);
2584*0Sstevel@tonic-gate 		*off_pp = &me->path_p_link;
2585*0Sstevel@tonic-gate 		me->path_snap_state &=
2586*0Sstevel@tonic-gate 		    ~(DI_PATH_SNAP_NOPHCI | DI_PATH_SNAP_NOPLINK);
2587*0Sstevel@tonic-gate 	}
2588*0Sstevel@tonic-gate }
2589*0Sstevel@tonic-gate 
2590*0Sstevel@tonic-gate /*
2591*0Sstevel@tonic-gate  * poff_p: pointer to the linkage field. This links pips along the client|phci
2592*0Sstevel@tonic-gate  *	   linkage list.
2593*0Sstevel@tonic-gate  * noff  : Offset for the endpoint dip snapshot.
2594*0Sstevel@tonic-gate  */
2595*0Sstevel@tonic-gate static di_off_t
2596*0Sstevel@tonic-gate di_getpath_data(dev_info_t *dip, di_off_t *poff_p, di_off_t noff,
2597*0Sstevel@tonic-gate     struct di_state *st, int get_client)
2598*0Sstevel@tonic-gate {
2599*0Sstevel@tonic-gate 	di_off_t off;
2600*0Sstevel@tonic-gate 	mdi_pathinfo_t *pip;
2601*0Sstevel@tonic-gate 	struct di_path *me;
2602*0Sstevel@tonic-gate 	mdi_pathinfo_t *(*next_pip)(dev_info_t *, mdi_pathinfo_t *);
2603*0Sstevel@tonic-gate 
2604*0Sstevel@tonic-gate 	dcmn_err2((CE_WARN, "di_getpath_data: client = %d", get_client));
2605*0Sstevel@tonic-gate 
2606*0Sstevel@tonic-gate 	/*
2607*0Sstevel@tonic-gate 	 * The naming of the following mdi_xyz() is unfortunately
2608*0Sstevel@tonic-gate 	 * non-intuitive. mdi_get_next_phci_path() follows the
2609*0Sstevel@tonic-gate 	 * client_link i.e. the list of pip's belonging to the
2610*0Sstevel@tonic-gate 	 * given client dip.
2611*0Sstevel@tonic-gate 	 */
2612*0Sstevel@tonic-gate 	if (get_client)
2613*0Sstevel@tonic-gate 		next_pip = &mdi_get_next_phci_path;
2614*0Sstevel@tonic-gate 	else
2615*0Sstevel@tonic-gate 		next_pip = &mdi_get_next_client_path;
2616*0Sstevel@tonic-gate 
2617*0Sstevel@tonic-gate 	off = *poff_p;
2618*0Sstevel@tonic-gate 
2619*0Sstevel@tonic-gate 	pip = NULL;
2620*0Sstevel@tonic-gate 	while (pip = (*next_pip)(dip, pip)) {
2621*0Sstevel@tonic-gate 		mdi_pathinfo_state_t state;
2622*0Sstevel@tonic-gate 		di_off_t stored_offset;
2623*0Sstevel@tonic-gate 
2624*0Sstevel@tonic-gate 		dcmn_err((CE_WARN, "marshalling pip = %p", (void *)pip));
2625*0Sstevel@tonic-gate 
2626*0Sstevel@tonic-gate 		mdi_pi_lock(pip);
2627*0Sstevel@tonic-gate 
2628*0Sstevel@tonic-gate 		if (di_pip_find(st, pip, &stored_offset) != -1) {
2629*0Sstevel@tonic-gate 			/*
2630*0Sstevel@tonic-gate 			 * We've already seen this pathinfo node so we need to
2631*0Sstevel@tonic-gate 			 * take care not to snap it again; However, one endpoint
2632*0Sstevel@tonic-gate 			 * and linkage will be set here. The other endpoint
2633*0Sstevel@tonic-gate 			 * and linkage has already been set when the pip was
2634*0Sstevel@tonic-gate 			 * first snapshotted i.e. when the other endpoint dip
2635*0Sstevel@tonic-gate 			 * was snapshotted.
2636*0Sstevel@tonic-gate 			 */
2637*0Sstevel@tonic-gate 			me = (struct di_path *)di_mem_addr(st, stored_offset);
2638*0Sstevel@tonic-gate 
2639*0Sstevel@tonic-gate 			*poff_p = stored_offset;
2640*0Sstevel@tonic-gate 
2641*0Sstevel@tonic-gate 			di_path_one_endpoint(me, noff, &poff_p, get_client);
2642*0Sstevel@tonic-gate 
2643*0Sstevel@tonic-gate 			/*
2644*0Sstevel@tonic-gate 			 * The other endpoint and linkage were set when this
2645*0Sstevel@tonic-gate 			 * pip was snapshotted. So we are done with both
2646*0Sstevel@tonic-gate 			 * endpoints and linkages.
2647*0Sstevel@tonic-gate 			 */
2648*0Sstevel@tonic-gate 			ASSERT(!(me->path_snap_state &
2649*0Sstevel@tonic-gate 			    (DI_PATH_SNAP_NOCLIENT|DI_PATH_SNAP_NOPHCI)));
2650*0Sstevel@tonic-gate 			ASSERT(!(me->path_snap_state &
2651*0Sstevel@tonic-gate 			    (DI_PATH_SNAP_NOCLINK|DI_PATH_SNAP_NOPLINK)));
2652*0Sstevel@tonic-gate 
2653*0Sstevel@tonic-gate 			mdi_pi_unlock(pip);
2654*0Sstevel@tonic-gate 			continue;
2655*0Sstevel@tonic-gate 		}
2656*0Sstevel@tonic-gate 
2657*0Sstevel@tonic-gate 		/*
2658*0Sstevel@tonic-gate 		 * Now that we need to snapshot this pip, check memory
2659*0Sstevel@tonic-gate 		 */
2660*0Sstevel@tonic-gate 		off = di_checkmem(st, off, sizeof (struct di_path));
2661*0Sstevel@tonic-gate 		me = (struct di_path *)di_mem_addr(st, off);
2662*0Sstevel@tonic-gate 		me->self = off;
2663*0Sstevel@tonic-gate 		*poff_p = off;
2664*0Sstevel@tonic-gate 		off += sizeof (struct di_path);
2665*0Sstevel@tonic-gate 
2666*0Sstevel@tonic-gate 		me->path_snap_state =
2667*0Sstevel@tonic-gate 		    DI_PATH_SNAP_NOCLINK | DI_PATH_SNAP_NOPLINK;
2668*0Sstevel@tonic-gate 		me->path_snap_state |=
2669*0Sstevel@tonic-gate 		    DI_PATH_SNAP_NOCLIENT | DI_PATH_SNAP_NOPHCI;
2670*0Sstevel@tonic-gate 
2671*0Sstevel@tonic-gate 		/*
2672*0Sstevel@tonic-gate 		 * Zero out fields as di_checkmem() doesn't guarantee
2673*0Sstevel@tonic-gate 		 * zero-filled memory
2674*0Sstevel@tonic-gate 		 */
2675*0Sstevel@tonic-gate 		me->path_client = me->path_phci = 0;
2676*0Sstevel@tonic-gate 		me->path_c_link = me->path_p_link = 0;
2677*0Sstevel@tonic-gate 
2678*0Sstevel@tonic-gate 		di_path_one_endpoint(me, noff, &poff_p, get_client);
2679*0Sstevel@tonic-gate 
2680*0Sstevel@tonic-gate 		/*
2681*0Sstevel@tonic-gate 		 * Note the existence of this pathinfo
2682*0Sstevel@tonic-gate 		 */
2683*0Sstevel@tonic-gate 		di_register_pip(st, pip, me->self);
2684*0Sstevel@tonic-gate 
2685*0Sstevel@tonic-gate 		state = mdi_pi_get_state(pip);
2686*0Sstevel@tonic-gate 		me->path_state = path_state_convert(state);
2687*0Sstevel@tonic-gate 
2688*0Sstevel@tonic-gate 		/*
2689*0Sstevel@tonic-gate 		 * Get intermediate addressing info.
2690*0Sstevel@tonic-gate 		 */
2691*0Sstevel@tonic-gate 		off = di_checkmem(st, off, strlen(mdi_pi_get_addr(pip)) + 1);
2692*0Sstevel@tonic-gate 		me->path_addr = off;
2693*0Sstevel@tonic-gate 		(void) strcpy(di_mem_addr(st, off), mdi_pi_get_addr(pip));
2694*0Sstevel@tonic-gate 		off += strlen(mdi_pi_get_addr(pip)) + 1;
2695*0Sstevel@tonic-gate 
2696*0Sstevel@tonic-gate 		/*
2697*0Sstevel@tonic-gate 		 * Get path properties if props are to be included in the
2698*0Sstevel@tonic-gate 		 * snapshot
2699*0Sstevel@tonic-gate 		 */
2700*0Sstevel@tonic-gate 		if (DINFOPROP & st->command) {
2701*0Sstevel@tonic-gate 			off = di_path_getprop(pip, off, &me->path_prop, st);
2702*0Sstevel@tonic-gate 		} else {
2703*0Sstevel@tonic-gate 			me->path_prop = 0;
2704*0Sstevel@tonic-gate 		}
2705*0Sstevel@tonic-gate 
2706*0Sstevel@tonic-gate 		mdi_pi_unlock(pip);
2707*0Sstevel@tonic-gate 	}
2708*0Sstevel@tonic-gate 
2709*0Sstevel@tonic-gate 	*poff_p = 0;
2710*0Sstevel@tonic-gate 
2711*0Sstevel@tonic-gate 	return (off);
2712*0Sstevel@tonic-gate }
2713*0Sstevel@tonic-gate 
2714*0Sstevel@tonic-gate /*
2715*0Sstevel@tonic-gate  * Copy a list of properties attached to a devinfo node. Called from
2716*0Sstevel@tonic-gate  * di_copynode with devi_lock held. The major number is passed in case
2717*0Sstevel@tonic-gate  * we need to call driver's prop_op entry. The value of list indicates
2718*0Sstevel@tonic-gate  * which list we are copying. Possible values are:
2719*0Sstevel@tonic-gate  * DI_PROP_DRV_LIST, DI_PROP_SYS_LIST, DI_PROP_GLB_LIST, DI_PROP_HW_LIST
2720*0Sstevel@tonic-gate  */
2721*0Sstevel@tonic-gate static di_off_t
2722*0Sstevel@tonic-gate di_getprop(struct ddi_prop *prop, di_off_t *off_p, struct di_state *st,
2723*0Sstevel@tonic-gate 	struct dev_info *dip, int list)
2724*0Sstevel@tonic-gate {
2725*0Sstevel@tonic-gate 	dev_t dev;
2726*0Sstevel@tonic-gate 	int (*prop_op)();
2727*0Sstevel@tonic-gate 	int off, need_prop_op = 0;
2728*0Sstevel@tonic-gate 	int prop_op_fail = 0;
2729*0Sstevel@tonic-gate 	ddi_prop_t *propp = NULL;
2730*0Sstevel@tonic-gate 	struct di_prop *pp;
2731*0Sstevel@tonic-gate 	struct dev_ops *ops = NULL;
2732*0Sstevel@tonic-gate 	int prop_len;
2733*0Sstevel@tonic-gate 	caddr_t prop_val;
2734*0Sstevel@tonic-gate 
2735*0Sstevel@tonic-gate 
2736*0Sstevel@tonic-gate 	dcmn_err2((CE_CONT, "di_getprop:\n"));
2737*0Sstevel@tonic-gate 
2738*0Sstevel@tonic-gate 	ASSERT(st != NULL);
2739*0Sstevel@tonic-gate 
2740*0Sstevel@tonic-gate 	dcmn_err((CE_CONT, "copy property list at addr %p\n", (void *)prop));
2741*0Sstevel@tonic-gate 
2742*0Sstevel@tonic-gate 	/*
2743*0Sstevel@tonic-gate 	 * Figure out if we need to call driver's prop_op entry point.
2744*0Sstevel@tonic-gate 	 * The conditions are:
2745*0Sstevel@tonic-gate 	 *	-- driver property list
2746*0Sstevel@tonic-gate 	 *	-- driver must be attached and held
2747*0Sstevel@tonic-gate 	 *	-- driver's cb_prop_op != ddi_prop_op
2748*0Sstevel@tonic-gate 	 *		or parent's bus_prop_op != ddi_bus_prop_op
2749*0Sstevel@tonic-gate 	 */
2750*0Sstevel@tonic-gate 
2751*0Sstevel@tonic-gate 	if (list != DI_PROP_DRV_LIST) {
2752*0Sstevel@tonic-gate 		goto getprop;
2753*0Sstevel@tonic-gate 	}
2754*0Sstevel@tonic-gate 
2755*0Sstevel@tonic-gate 	/*
2756*0Sstevel@tonic-gate 	 * If driver is not attached or if major is -1, we ignore
2757*0Sstevel@tonic-gate 	 * the driver property list. No one should rely on such
2758*0Sstevel@tonic-gate 	 * properties.
2759*0Sstevel@tonic-gate 	 */
2760*0Sstevel@tonic-gate 	if (i_ddi_node_state((dev_info_t *)dip) < DS_ATTACHED) {
2761*0Sstevel@tonic-gate 		off = *off_p;
2762*0Sstevel@tonic-gate 		*off_p = 0;
2763*0Sstevel@tonic-gate 		return (off);
2764*0Sstevel@tonic-gate 	}
2765*0Sstevel@tonic-gate 
2766*0Sstevel@tonic-gate 	/*
2767*0Sstevel@tonic-gate 	 * Now we have a driver which is held. We can examine entry points
2768*0Sstevel@tonic-gate 	 * and check the condition listed above.
2769*0Sstevel@tonic-gate 	 */
2770*0Sstevel@tonic-gate 	ops = dip->devi_ops;
2771*0Sstevel@tonic-gate 
2772*0Sstevel@tonic-gate 	/*
2773*0Sstevel@tonic-gate 	 * Some nexus drivers incorrectly set cb_prop_op to nodev,
2774*0Sstevel@tonic-gate 	 * nulldev or even NULL.
2775*0Sstevel@tonic-gate 	 */
2776*0Sstevel@tonic-gate 	if (ops && ops->devo_cb_ops &&
2777*0Sstevel@tonic-gate 	    (ops->devo_cb_ops->cb_prop_op != ddi_prop_op) &&
2778*0Sstevel@tonic-gate 	    (ops->devo_cb_ops->cb_prop_op != nodev) &&
2779*0Sstevel@tonic-gate 	    (ops->devo_cb_ops->cb_prop_op != nulldev) &&
2780*0Sstevel@tonic-gate 	    (ops->devo_cb_ops->cb_prop_op != NULL)) {
2781*0Sstevel@tonic-gate 		need_prop_op = 1;
2782*0Sstevel@tonic-gate 	}
2783*0Sstevel@tonic-gate 
2784*0Sstevel@tonic-gate getprop:
2785*0Sstevel@tonic-gate 	/*
2786*0Sstevel@tonic-gate 	 * check memory availability
2787*0Sstevel@tonic-gate 	 */
2788*0Sstevel@tonic-gate 	off = di_checkmem(st, *off_p, sizeof (struct di_prop));
2789*0Sstevel@tonic-gate 	*off_p = off;
2790*0Sstevel@tonic-gate 	/*
2791*0Sstevel@tonic-gate 	 * Now copy properties
2792*0Sstevel@tonic-gate 	 */
2793*0Sstevel@tonic-gate 	do {
2794*0Sstevel@tonic-gate 		pp = (struct di_prop *)di_mem_addr(st, off);
2795*0Sstevel@tonic-gate 		pp->self = off;
2796*0Sstevel@tonic-gate 		/*
2797*0Sstevel@tonic-gate 		 * Split dev_t to major/minor, so it works for
2798*0Sstevel@tonic-gate 		 * both ILP32 and LP64 model
2799*0Sstevel@tonic-gate 		 */
2800*0Sstevel@tonic-gate 		pp->dev_major = getmajor(prop->prop_dev);
2801*0Sstevel@tonic-gate 		pp->dev_minor = getminor(prop->prop_dev);
2802*0Sstevel@tonic-gate 		pp->prop_flags = prop->prop_flags;
2803*0Sstevel@tonic-gate 		pp->prop_list = list;
2804*0Sstevel@tonic-gate 
2805*0Sstevel@tonic-gate 		/*
2806*0Sstevel@tonic-gate 		 * property name
2807*0Sstevel@tonic-gate 		 */
2808*0Sstevel@tonic-gate 		off += sizeof (struct di_prop);
2809*0Sstevel@tonic-gate 		if (prop->prop_name) {
2810*0Sstevel@tonic-gate 			off = di_checkmem(st, off, strlen(prop->prop_name)
2811*0Sstevel@tonic-gate 			    + 1);
2812*0Sstevel@tonic-gate 			pp->prop_name = off;
2813*0Sstevel@tonic-gate 			(void) strcpy(di_mem_addr(st, off), prop->prop_name);
2814*0Sstevel@tonic-gate 			off += strlen(prop->prop_name) + 1;
2815*0Sstevel@tonic-gate 		}
2816*0Sstevel@tonic-gate 
2817*0Sstevel@tonic-gate 		/*
2818*0Sstevel@tonic-gate 		 * Set prop_len here. This may change later
2819*0Sstevel@tonic-gate 		 * if cb_prop_op returns a different length.
2820*0Sstevel@tonic-gate 		 */
2821*0Sstevel@tonic-gate 		pp->prop_len = prop->prop_len;
2822*0Sstevel@tonic-gate 		if (!need_prop_op) {
2823*0Sstevel@tonic-gate 			if (prop->prop_val == NULL) {
2824*0Sstevel@tonic-gate 				dcmn_err((CE_WARN,
2825*0Sstevel@tonic-gate 				    "devinfo: property fault at %p",
2826*0Sstevel@tonic-gate 				    (void *)prop));
2827*0Sstevel@tonic-gate 				pp->prop_data = -1;
2828*0Sstevel@tonic-gate 			} else if (prop->prop_len != 0) {
2829*0Sstevel@tonic-gate 				off = di_checkmem(st, off, prop->prop_len);
2830*0Sstevel@tonic-gate 				pp->prop_data = off;
2831*0Sstevel@tonic-gate 				bcopy(prop->prop_val, di_mem_addr(st, off),
2832*0Sstevel@tonic-gate 				    prop->prop_len);
2833*0Sstevel@tonic-gate 				off += DI_ALIGN(pp->prop_len);
2834*0Sstevel@tonic-gate 			}
2835*0Sstevel@tonic-gate 		}
2836*0Sstevel@tonic-gate 
2837*0Sstevel@tonic-gate 		off = di_checkmem(st, off, sizeof (struct di_prop));
2838*0Sstevel@tonic-gate 		pp->next = off;
2839*0Sstevel@tonic-gate 		prop = prop->prop_next;
2840*0Sstevel@tonic-gate 	} while (prop);
2841*0Sstevel@tonic-gate 
2842*0Sstevel@tonic-gate 	pp->next = 0;
2843*0Sstevel@tonic-gate 
2844*0Sstevel@tonic-gate 	if (!need_prop_op) {
2845*0Sstevel@tonic-gate 		dcmn_err((CE_CONT, "finished property "
2846*0Sstevel@tonic-gate 		    "list at offset 0x%x\n", off));
2847*0Sstevel@tonic-gate 		return (off);
2848*0Sstevel@tonic-gate 	}
2849*0Sstevel@tonic-gate 
2850*0Sstevel@tonic-gate 	/*
2851*0Sstevel@tonic-gate 	 * If there is a need to call driver's prop_op entry,
2852*0Sstevel@tonic-gate 	 * we must release driver's devi_lock, because the
2853*0Sstevel@tonic-gate 	 * cb_prop_op entry point will grab it.
2854*0Sstevel@tonic-gate 	 *
2855*0Sstevel@tonic-gate 	 * The snapshot memory has already been allocated above,
2856*0Sstevel@tonic-gate 	 * which means the length of an active property should
2857*0Sstevel@tonic-gate 	 * remain fixed for this implementation to work.
2858*0Sstevel@tonic-gate 	 */
2859*0Sstevel@tonic-gate 
2860*0Sstevel@tonic-gate 
2861*0Sstevel@tonic-gate 	prop_op = ops->devo_cb_ops->cb_prop_op;
2862*0Sstevel@tonic-gate 	pp = (struct di_prop *)di_mem_addr(st, *off_p);
2863*0Sstevel@tonic-gate 
2864*0Sstevel@tonic-gate 	mutex_exit(&dip->devi_lock);
2865*0Sstevel@tonic-gate 
2866*0Sstevel@tonic-gate 	do {
2867*0Sstevel@tonic-gate 		int err;
2868*0Sstevel@tonic-gate 		struct di_prop *tmp;
2869*0Sstevel@tonic-gate 
2870*0Sstevel@tonic-gate 		if (pp->next) {
2871*0Sstevel@tonic-gate 			tmp = (struct di_prop *)
2872*0Sstevel@tonic-gate 			    di_mem_addr(st, pp->next);
2873*0Sstevel@tonic-gate 		} else {
2874*0Sstevel@tonic-gate 			tmp = NULL;
2875*0Sstevel@tonic-gate 		}
2876*0Sstevel@tonic-gate 
2877*0Sstevel@tonic-gate 		/*
2878*0Sstevel@tonic-gate 		 * call into driver's prop_op entry point
2879*0Sstevel@tonic-gate 		 *
2880*0Sstevel@tonic-gate 		 * Must search DDI_DEV_T_NONE with DDI_DEV_T_ANY
2881*0Sstevel@tonic-gate 		 */
2882*0Sstevel@tonic-gate 		dev = makedevice(pp->dev_major, pp->dev_minor);
2883*0Sstevel@tonic-gate 		if (dev == DDI_DEV_T_NONE)
2884*0Sstevel@tonic-gate 			dev = DDI_DEV_T_ANY;
2885*0Sstevel@tonic-gate 
2886*0Sstevel@tonic-gate 		dcmn_err((CE_CONT, "call prop_op"
2887*0Sstevel@tonic-gate 		    "(%lx, %p, PROP_LEN_AND_VAL_BUF, "
2888*0Sstevel@tonic-gate 		    "DDI_PROP_DONTPASS, \"%s\", %p, &%d)\n",
2889*0Sstevel@tonic-gate 		    dev,
2890*0Sstevel@tonic-gate 		    (void *)dip,
2891*0Sstevel@tonic-gate 		    (char *)di_mem_addr(st, pp->prop_name),
2892*0Sstevel@tonic-gate 		    (void *)di_mem_addr(st, pp->prop_data),
2893*0Sstevel@tonic-gate 		    pp->prop_len));
2894*0Sstevel@tonic-gate 
2895*0Sstevel@tonic-gate 		if ((err = (*prop_op)(dev, (dev_info_t)dip,
2896*0Sstevel@tonic-gate 		    PROP_LEN_AND_VAL_ALLOC, DDI_PROP_DONTPASS,
2897*0Sstevel@tonic-gate 		    (char *)di_mem_addr(st, pp->prop_name),
2898*0Sstevel@tonic-gate 		    &prop_val, &prop_len)) != DDI_PROP_SUCCESS) {
2899*0Sstevel@tonic-gate 			if ((propp = i_ddi_prop_search(dev,
2900*0Sstevel@tonic-gate 			    (char *)di_mem_addr(st, pp->prop_name),
2901*0Sstevel@tonic-gate 			    (uint_t)pp->prop_flags,
2902*0Sstevel@tonic-gate 			    &(DEVI(dip)->devi_drv_prop_ptr))) != NULL) {
2903*0Sstevel@tonic-gate 				pp->prop_len = propp->prop_len;
2904*0Sstevel@tonic-gate 				if (pp->prop_len != 0) {
2905*0Sstevel@tonic-gate 					off = di_checkmem(st, off,
2906*0Sstevel@tonic-gate 					    pp->prop_len);
2907*0Sstevel@tonic-gate 					pp->prop_data = off;
2908*0Sstevel@tonic-gate 					bcopy(propp->prop_val, di_mem_addr(st,
2909*0Sstevel@tonic-gate 					    pp->prop_data), propp->prop_len);
2910*0Sstevel@tonic-gate 					off += DI_ALIGN(pp->prop_len);
2911*0Sstevel@tonic-gate 				}
2912*0Sstevel@tonic-gate 			} else {
2913*0Sstevel@tonic-gate 				prop_op_fail = 1;
2914*0Sstevel@tonic-gate 			}
2915*0Sstevel@tonic-gate 		} else if (prop_len != 0) {
2916*0Sstevel@tonic-gate 			pp->prop_len = prop_len;
2917*0Sstevel@tonic-gate 			off = di_checkmem(st, off, prop_len);
2918*0Sstevel@tonic-gate 			pp->prop_data = off;
2919*0Sstevel@tonic-gate 			bcopy(prop_val, di_mem_addr(st, off), prop_len);
2920*0Sstevel@tonic-gate 			off += DI_ALIGN(prop_len);
2921*0Sstevel@tonic-gate 			kmem_free(prop_val, prop_len);
2922*0Sstevel@tonic-gate 		}
2923*0Sstevel@tonic-gate 
2924*0Sstevel@tonic-gate 		if (prop_op_fail) {
2925*0Sstevel@tonic-gate 			pp->prop_data = -1;
2926*0Sstevel@tonic-gate 			dcmn_err((CE_WARN, "devinfo: prop_op failure "
2927*0Sstevel@tonic-gate 			    "for \"%s\" err %d",
2928*0Sstevel@tonic-gate 			    di_mem_addr(st, pp->prop_name), err));
2929*0Sstevel@tonic-gate 		}
2930*0Sstevel@tonic-gate 
2931*0Sstevel@tonic-gate 		pp = tmp;
2932*0Sstevel@tonic-gate 
2933*0Sstevel@tonic-gate 	} while (pp);
2934*0Sstevel@tonic-gate 
2935*0Sstevel@tonic-gate 	mutex_enter(&dip->devi_lock);
2936*0Sstevel@tonic-gate 	dcmn_err((CE_CONT, "finished property list at offset 0x%x\n", off));
2937*0Sstevel@tonic-gate 	return (off);
2938*0Sstevel@tonic-gate }
2939*0Sstevel@tonic-gate 
2940*0Sstevel@tonic-gate /*
2941*0Sstevel@tonic-gate  * find private data format attached to a dip
2942*0Sstevel@tonic-gate  * parent = 1 to match driver name of parent dip (for parent private data)
2943*0Sstevel@tonic-gate  *	0 to match driver name of current dip (for driver private data)
2944*0Sstevel@tonic-gate  */
2945*0Sstevel@tonic-gate #define	DI_MATCH_DRIVER	0
2946*0Sstevel@tonic-gate #define	DI_MATCH_PARENT	1
2947*0Sstevel@tonic-gate 
2948*0Sstevel@tonic-gate struct di_priv_format *
2949*0Sstevel@tonic-gate di_match_drv_name(struct dev_info *node, struct di_state *st, int match)
2950*0Sstevel@tonic-gate {
2951*0Sstevel@tonic-gate 	int i, count, len;
2952*0Sstevel@tonic-gate 	char *drv_name;
2953*0Sstevel@tonic-gate 	major_t major;
2954*0Sstevel@tonic-gate 	struct di_all *all;
2955*0Sstevel@tonic-gate 	struct di_priv_format *form;
2956*0Sstevel@tonic-gate 
2957*0Sstevel@tonic-gate 	dcmn_err2((CE_CONT, "di_match_drv_name: node = %s, match = %x\n",
2958*0Sstevel@tonic-gate 		node->devi_node_name, match));
2959*0Sstevel@tonic-gate 
2960*0Sstevel@tonic-gate 	if (match == DI_MATCH_PARENT) {
2961*0Sstevel@tonic-gate 		node = DEVI(node->devi_parent);
2962*0Sstevel@tonic-gate 	}
2963*0Sstevel@tonic-gate 
2964*0Sstevel@tonic-gate 	if (node == NULL) {
2965*0Sstevel@tonic-gate 		return (NULL);
2966*0Sstevel@tonic-gate 	}
2967*0Sstevel@tonic-gate 
2968*0Sstevel@tonic-gate 	major = ddi_name_to_major(node->devi_binding_name);
2969*0Sstevel@tonic-gate 	if (major == (major_t)(-1)) {
2970*0Sstevel@tonic-gate 		return (NULL);
2971*0Sstevel@tonic-gate 	}
2972*0Sstevel@tonic-gate 
2973*0Sstevel@tonic-gate 	/*
2974*0Sstevel@tonic-gate 	 * Match the driver name.
2975*0Sstevel@tonic-gate 	 */
2976*0Sstevel@tonic-gate 	drv_name = ddi_major_to_name(major);
2977*0Sstevel@tonic-gate 	if ((drv_name == NULL) || *drv_name == '\0') {
2978*0Sstevel@tonic-gate 		return (NULL);
2979*0Sstevel@tonic-gate 	}
2980*0Sstevel@tonic-gate 
2981*0Sstevel@tonic-gate 	/* Now get the di_priv_format array */
2982*0Sstevel@tonic-gate 	all = (struct di_all *)di_mem_addr(st, 0);
2983*0Sstevel@tonic-gate 
2984*0Sstevel@tonic-gate 	if (match == DI_MATCH_PARENT) {
2985*0Sstevel@tonic-gate 		count = all->n_ppdata;
2986*0Sstevel@tonic-gate 		form = (struct di_priv_format *)
2987*0Sstevel@tonic-gate 			(di_mem_addr(st, 0) + all->ppdata_format);
2988*0Sstevel@tonic-gate 	} else {
2989*0Sstevel@tonic-gate 		count = all->n_dpdata;
2990*0Sstevel@tonic-gate 		form = (struct di_priv_format *)
2991*0Sstevel@tonic-gate 			((caddr_t)all + all->dpdata_format);
2992*0Sstevel@tonic-gate 	}
2993*0Sstevel@tonic-gate 
2994*0Sstevel@tonic-gate 	len = strlen(drv_name);
2995*0Sstevel@tonic-gate 	for (i = 0; i < count; i++) {
2996*0Sstevel@tonic-gate 		char *tmp;
2997*0Sstevel@tonic-gate 
2998*0Sstevel@tonic-gate 		tmp = form[i].drv_name;
2999*0Sstevel@tonic-gate 		while (tmp && (*tmp != '\0')) {
3000*0Sstevel@tonic-gate 			if (strncmp(drv_name, tmp, len) == 0) {
3001*0Sstevel@tonic-gate 				return (&form[i]);
3002*0Sstevel@tonic-gate 			}
3003*0Sstevel@tonic-gate 			/*
3004*0Sstevel@tonic-gate 			 * Move to next driver name, skipping a white space
3005*0Sstevel@tonic-gate 			 */
3006*0Sstevel@tonic-gate 			if (tmp = strchr(tmp, ' ')) {
3007*0Sstevel@tonic-gate 				tmp++;
3008*0Sstevel@tonic-gate 			}
3009*0Sstevel@tonic-gate 		}
3010*0Sstevel@tonic-gate 	}
3011*0Sstevel@tonic-gate 
3012*0Sstevel@tonic-gate 	return (NULL);
3013*0Sstevel@tonic-gate }
3014*0Sstevel@tonic-gate 
3015*0Sstevel@tonic-gate /*
3016*0Sstevel@tonic-gate  * The following functions copy data as specified by the format passed in.
3017*0Sstevel@tonic-gate  * To prevent invalid format from panicing the system, we call on_fault().
3018*0Sstevel@tonic-gate  * A return value of 0 indicates an error. Otherwise, the total offset
3019*0Sstevel@tonic-gate  * is returned.
3020*0Sstevel@tonic-gate  */
3021*0Sstevel@tonic-gate #define	DI_MAX_PRIVDATA	(PAGESIZE >> 1)	/* max private data size */
3022*0Sstevel@tonic-gate 
3023*0Sstevel@tonic-gate static di_off_t
3024*0Sstevel@tonic-gate di_getprvdata(struct di_priv_format *pdp, void *data, di_off_t *off_p,
3025*0Sstevel@tonic-gate 	struct di_state *st)
3026*0Sstevel@tonic-gate {
3027*0Sstevel@tonic-gate 	caddr_t pa;
3028*0Sstevel@tonic-gate 	void *ptr;
3029*0Sstevel@tonic-gate 	int i, size, repeat;
3030*0Sstevel@tonic-gate 	di_off_t off, off0, *tmp;
3031*0Sstevel@tonic-gate 
3032*0Sstevel@tonic-gate 	label_t ljb;
3033*0Sstevel@tonic-gate 
3034*0Sstevel@tonic-gate 	dcmn_err2((CE_CONT, "di_getprvdata:\n"));
3035*0Sstevel@tonic-gate 
3036*0Sstevel@tonic-gate 	/*
3037*0Sstevel@tonic-gate 	 * check memory availability. Private data size is
3038*0Sstevel@tonic-gate 	 * limited to DI_MAX_PRIVDATA.
3039*0Sstevel@tonic-gate 	 */
3040*0Sstevel@tonic-gate 	off = di_checkmem(st, *off_p, DI_MAX_PRIVDATA);
3041*0Sstevel@tonic-gate 
3042*0Sstevel@tonic-gate 	if ((pdp->bytes <= 0) || pdp->bytes > DI_MAX_PRIVDATA) {
3043*0Sstevel@tonic-gate 		goto failure;
3044*0Sstevel@tonic-gate 	}
3045*0Sstevel@tonic-gate 
3046*0Sstevel@tonic-gate 	if (!on_fault(&ljb)) {
3047*0Sstevel@tonic-gate 		/* copy the struct */
3048*0Sstevel@tonic-gate 		bcopy(data, di_mem_addr(st, off), pdp->bytes);
3049*0Sstevel@tonic-gate 		off0 = DI_ALIGN(pdp->bytes);
3050*0Sstevel@tonic-gate 
3051*0Sstevel@tonic-gate 		/* dereferencing pointers */
3052*0Sstevel@tonic-gate 		for (i = 0; i < MAX_PTR_IN_PRV; i++) {
3053*0Sstevel@tonic-gate 
3054*0Sstevel@tonic-gate 			if (pdp->ptr[i].size == 0) {
3055*0Sstevel@tonic-gate 				goto success;	/* no more ptrs */
3056*0Sstevel@tonic-gate 			}
3057*0Sstevel@tonic-gate 
3058*0Sstevel@tonic-gate 			/*
3059*0Sstevel@tonic-gate 			 * first, get the pointer content
3060*0Sstevel@tonic-gate 			 */
3061*0Sstevel@tonic-gate 			if ((pdp->ptr[i].offset < 0) ||
3062*0Sstevel@tonic-gate 				(pdp->ptr[i].offset >
3063*0Sstevel@tonic-gate 				pdp->bytes - sizeof (char *)))
3064*0Sstevel@tonic-gate 				goto failure;	/* wrong offset */
3065*0Sstevel@tonic-gate 
3066*0Sstevel@tonic-gate 			pa = di_mem_addr(st, off + pdp->ptr[i].offset);
3067*0Sstevel@tonic-gate 			tmp = (di_off_t *)pa;	/* to store off_t later */
3068*0Sstevel@tonic-gate 
3069*0Sstevel@tonic-gate 			ptr = *((void **) pa);	/* get pointer value */
3070*0Sstevel@tonic-gate 			if (ptr == NULL) {	/* if NULL pointer, go on */
3071*0Sstevel@tonic-gate 				continue;
3072*0Sstevel@tonic-gate 			}
3073*0Sstevel@tonic-gate 
3074*0Sstevel@tonic-gate 			/*
3075*0Sstevel@tonic-gate 			 * next, find the repeat count (array dimension)
3076*0Sstevel@tonic-gate 			 */
3077*0Sstevel@tonic-gate 			repeat = pdp->ptr[i].len_offset;
3078*0Sstevel@tonic-gate 
3079*0Sstevel@tonic-gate 			/*
3080*0Sstevel@tonic-gate 			 * Positive value indicates a fixed sized array.
3081*0Sstevel@tonic-gate 			 * 0 or negative value indicates variable sized array.
3082*0Sstevel@tonic-gate 			 *
3083*0Sstevel@tonic-gate 			 * For variable sized array, the variable must be
3084*0Sstevel@tonic-gate 			 * an int member of the structure, with an offset
3085*0Sstevel@tonic-gate 			 * equal to the absolution value of struct member.
3086*0Sstevel@tonic-gate 			 */
3087*0Sstevel@tonic-gate 			if (repeat > pdp->bytes - sizeof (int)) {
3088*0Sstevel@tonic-gate 				goto failure;	/* wrong offset */
3089*0Sstevel@tonic-gate 			}
3090*0Sstevel@tonic-gate 
3091*0Sstevel@tonic-gate 			if (repeat >= 0) {
3092*0Sstevel@tonic-gate 				repeat = *((int *)((caddr_t)data + repeat));
3093*0Sstevel@tonic-gate 			} else {
3094*0Sstevel@tonic-gate 				repeat = -repeat;
3095*0Sstevel@tonic-gate 			}
3096*0Sstevel@tonic-gate 
3097*0Sstevel@tonic-gate 			/*
3098*0Sstevel@tonic-gate 			 * next, get the size of the object to be copied
3099*0Sstevel@tonic-gate 			 */
3100*0Sstevel@tonic-gate 			size = pdp->ptr[i].size * repeat;
3101*0Sstevel@tonic-gate 
3102*0Sstevel@tonic-gate 			/*
3103*0Sstevel@tonic-gate 			 * Arbitrarily limit the total size of object to be
3104*0Sstevel@tonic-gate 			 * copied (1 byte to 1/4 page).
3105*0Sstevel@tonic-gate 			 */
3106*0Sstevel@tonic-gate 			if ((size <= 0) || (size > (DI_MAX_PRIVDATA - off0))) {
3107*0Sstevel@tonic-gate 				goto failure;	/* wrong size or too big */
3108*0Sstevel@tonic-gate 			}
3109*0Sstevel@tonic-gate 
3110*0Sstevel@tonic-gate 			/*
3111*0Sstevel@tonic-gate 			 * Now copy the data
3112*0Sstevel@tonic-gate 			 */
3113*0Sstevel@tonic-gate 			*tmp = off0;
3114*0Sstevel@tonic-gate 			bcopy(ptr, di_mem_addr(st, off + off0), size);
3115*0Sstevel@tonic-gate 			off0 += DI_ALIGN(size);
3116*0Sstevel@tonic-gate 		}
3117*0Sstevel@tonic-gate 	} else {
3118*0Sstevel@tonic-gate 		goto failure;
3119*0Sstevel@tonic-gate 	}
3120*0Sstevel@tonic-gate 
3121*0Sstevel@tonic-gate success:
3122*0Sstevel@tonic-gate 	/*
3123*0Sstevel@tonic-gate 	 * success if reached here
3124*0Sstevel@tonic-gate 	 */
3125*0Sstevel@tonic-gate 	no_fault();
3126*0Sstevel@tonic-gate 	*off_p = off;
3127*0Sstevel@tonic-gate 
3128*0Sstevel@tonic-gate 	return (off + off0);
3129*0Sstevel@tonic-gate 	/*NOTREACHED*/
3130*0Sstevel@tonic-gate 
3131*0Sstevel@tonic-gate failure:
3132*0Sstevel@tonic-gate 	/*
3133*0Sstevel@tonic-gate 	 * fault occurred
3134*0Sstevel@tonic-gate 	 */
3135*0Sstevel@tonic-gate 	no_fault();
3136*0Sstevel@tonic-gate 	cmn_err(CE_WARN, "devinfo: fault in private data at %p", data);
3137*0Sstevel@tonic-gate 	*off_p = -1;	/* set private data to indicate error */
3138*0Sstevel@tonic-gate 
3139*0Sstevel@tonic-gate 	return (off);
3140*0Sstevel@tonic-gate }
3141*0Sstevel@tonic-gate 
3142*0Sstevel@tonic-gate /*
3143*0Sstevel@tonic-gate  * get parent private data; on error, returns original offset
3144*0Sstevel@tonic-gate  */
3145*0Sstevel@tonic-gate static di_off_t
3146*0Sstevel@tonic-gate di_getppdata(struct dev_info *node, di_off_t *off_p, struct di_state *st)
3147*0Sstevel@tonic-gate {
3148*0Sstevel@tonic-gate 	int off;
3149*0Sstevel@tonic-gate 	struct di_priv_format *ppdp;
3150*0Sstevel@tonic-gate 
3151*0Sstevel@tonic-gate 	dcmn_err2((CE_CONT, "di_getppdata:\n"));
3152*0Sstevel@tonic-gate 
3153*0Sstevel@tonic-gate 	/* find the parent data format */
3154*0Sstevel@tonic-gate 	if ((ppdp = di_match_drv_name(node, st, DI_MATCH_PARENT)) == NULL) {
3155*0Sstevel@tonic-gate 		off = *off_p;
3156*0Sstevel@tonic-gate 		*off_p = 0;	/* set parent data to none */
3157*0Sstevel@tonic-gate 		return (off);
3158*0Sstevel@tonic-gate 	}
3159*0Sstevel@tonic-gate 
3160*0Sstevel@tonic-gate 	return (di_getprvdata(ppdp, ddi_get_parent_data((dev_info_t *)node),
3161*0Sstevel@tonic-gate 	    off_p, st));
3162*0Sstevel@tonic-gate }
3163*0Sstevel@tonic-gate 
3164*0Sstevel@tonic-gate /*
3165*0Sstevel@tonic-gate  * get parent private data; returns original offset
3166*0Sstevel@tonic-gate  */
3167*0Sstevel@tonic-gate static di_off_t
3168*0Sstevel@tonic-gate di_getdpdata(struct dev_info *node, di_off_t *off_p, struct di_state *st)
3169*0Sstevel@tonic-gate {
3170*0Sstevel@tonic-gate 	int off;
3171*0Sstevel@tonic-gate 	struct di_priv_format *dpdp;
3172*0Sstevel@tonic-gate 
3173*0Sstevel@tonic-gate 	dcmn_err2((CE_CONT, "di_getdpdata:"));
3174*0Sstevel@tonic-gate 
3175*0Sstevel@tonic-gate 	/* find the parent data format */
3176*0Sstevel@tonic-gate 	if ((dpdp = di_match_drv_name(node, st, DI_MATCH_DRIVER)) == NULL) {
3177*0Sstevel@tonic-gate 		off = *off_p;
3178*0Sstevel@tonic-gate 		*off_p = 0;	/* set driver data to none */
3179*0Sstevel@tonic-gate 		return (off);
3180*0Sstevel@tonic-gate 	}
3181*0Sstevel@tonic-gate 
3182*0Sstevel@tonic-gate 	return (di_getprvdata(dpdp, ddi_get_driver_private((dev_info_t *)node),
3183*0Sstevel@tonic-gate 	    off_p, st));
3184*0Sstevel@tonic-gate }
3185*0Sstevel@tonic-gate 
3186*0Sstevel@tonic-gate /*
3187*0Sstevel@tonic-gate  * The driver is stateful across DINFOCPYALL and DINFOUSRLD.
3188*0Sstevel@tonic-gate  * This function encapsulates the state machine:
3189*0Sstevel@tonic-gate  *
3190*0Sstevel@tonic-gate  *	-> IOC_IDLE -> IOC_SNAP -> IOC_DONE -> IOC_COPY ->
3191*0Sstevel@tonic-gate  *	|		SNAPSHOT		USRLD	 |
3192*0Sstevel@tonic-gate  *	--------------------------------------------------
3193*0Sstevel@tonic-gate  *
3194*0Sstevel@tonic-gate  * Returns 0 on success and -1 on failure
3195*0Sstevel@tonic-gate  */
3196*0Sstevel@tonic-gate static int
3197*0Sstevel@tonic-gate di_setstate(struct di_state *st, int new_state)
3198*0Sstevel@tonic-gate {
3199*0Sstevel@tonic-gate 	int ret = 0;
3200*0Sstevel@tonic-gate 
3201*0Sstevel@tonic-gate 	mutex_enter(&di_lock);
3202*0Sstevel@tonic-gate 	switch (new_state) {
3203*0Sstevel@tonic-gate 	case IOC_IDLE:
3204*0Sstevel@tonic-gate 	case IOC_DONE:
3205*0Sstevel@tonic-gate 		break;
3206*0Sstevel@tonic-gate 	case IOC_SNAP:
3207*0Sstevel@tonic-gate 		if (st->di_iocstate != IOC_IDLE)
3208*0Sstevel@tonic-gate 			ret = -1;
3209*0Sstevel@tonic-gate 		break;
3210*0Sstevel@tonic-gate 	case IOC_COPY:
3211*0Sstevel@tonic-gate 		if (st->di_iocstate != IOC_DONE)
3212*0Sstevel@tonic-gate 			ret = -1;
3213*0Sstevel@tonic-gate 		break;
3214*0Sstevel@tonic-gate 	default:
3215*0Sstevel@tonic-gate 		ret = -1;
3216*0Sstevel@tonic-gate 	}
3217*0Sstevel@tonic-gate 
3218*0Sstevel@tonic-gate 	if (ret == 0)
3219*0Sstevel@tonic-gate 		st->di_iocstate = new_state;
3220*0Sstevel@tonic-gate 	else
3221*0Sstevel@tonic-gate 		cmn_err(CE_NOTE, "incorrect state transition from %d to %d",
3222*0Sstevel@tonic-gate 		    st->di_iocstate, new_state);
3223*0Sstevel@tonic-gate 	mutex_exit(&di_lock);
3224*0Sstevel@tonic-gate 	return (ret);
3225*0Sstevel@tonic-gate }
3226*0Sstevel@tonic-gate 
3227*0Sstevel@tonic-gate /*
3228*0Sstevel@tonic-gate  * We cannot assume the presence of the entire
3229*0Sstevel@tonic-gate  * snapshot in this routine. All we are guaranteed
3230*0Sstevel@tonic-gate  * is the di_all struct + 1 byte (for root_path)
3231*0Sstevel@tonic-gate  */
3232*0Sstevel@tonic-gate static int
3233*0Sstevel@tonic-gate header_plus_one_ok(struct di_all *all)
3234*0Sstevel@tonic-gate {
3235*0Sstevel@tonic-gate 	/*
3236*0Sstevel@tonic-gate 	 * Refuse to read old versions
3237*0Sstevel@tonic-gate 	 */
3238*0Sstevel@tonic-gate 	if (all->version != DI_SNAPSHOT_VERSION) {
3239*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "bad version: 0x%x", all->version));
3240*0Sstevel@tonic-gate 		return (0);
3241*0Sstevel@tonic-gate 	}
3242*0Sstevel@tonic-gate 
3243*0Sstevel@tonic-gate 	if (all->cache_magic != DI_CACHE_MAGIC) {
3244*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "bad magic #: 0x%x", all->cache_magic));
3245*0Sstevel@tonic-gate 		return (0);
3246*0Sstevel@tonic-gate 	}
3247*0Sstevel@tonic-gate 
3248*0Sstevel@tonic-gate 	if (all->snapshot_time <= 0) {
3249*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "bad timestamp: %ld", all->snapshot_time));
3250*0Sstevel@tonic-gate 		return (0);
3251*0Sstevel@tonic-gate 	}
3252*0Sstevel@tonic-gate 
3253*0Sstevel@tonic-gate 	if (all->top_devinfo == 0) {
3254*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "NULL top devinfo"));
3255*0Sstevel@tonic-gate 		return (0);
3256*0Sstevel@tonic-gate 	}
3257*0Sstevel@tonic-gate 
3258*0Sstevel@tonic-gate 	if (all->map_size < sizeof (*all) + 1) {
3259*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "bad map size: %u", all->map_size));
3260*0Sstevel@tonic-gate 		return (0);
3261*0Sstevel@tonic-gate 	}
3262*0Sstevel@tonic-gate 
3263*0Sstevel@tonic-gate 	if (all->root_path[0] != '/' || all->root_path[1] != '\0') {
3264*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "bad rootpath: %c%c",
3265*0Sstevel@tonic-gate 		    all->root_path[0], all->root_path[1]));
3266*0Sstevel@tonic-gate 		return (0);
3267*0Sstevel@tonic-gate 	}
3268*0Sstevel@tonic-gate 
3269*0Sstevel@tonic-gate 	/*
3270*0Sstevel@tonic-gate 	 * We can't check checksum here as we just have the header
3271*0Sstevel@tonic-gate 	 */
3272*0Sstevel@tonic-gate 
3273*0Sstevel@tonic-gate 	return (1);
3274*0Sstevel@tonic-gate }
3275*0Sstevel@tonic-gate 
3276*0Sstevel@tonic-gate static int
3277*0Sstevel@tonic-gate chunk_write(struct vnode *vp, offset_t off, caddr_t buf, size_t len)
3278*0Sstevel@tonic-gate {
3279*0Sstevel@tonic-gate 	rlim64_t	rlimit;
3280*0Sstevel@tonic-gate 	ssize_t		resid;
3281*0Sstevel@tonic-gate 	int		error = 0;
3282*0Sstevel@tonic-gate 
3283*0Sstevel@tonic-gate 
3284*0Sstevel@tonic-gate 	rlimit = RLIM64_INFINITY;
3285*0Sstevel@tonic-gate 
3286*0Sstevel@tonic-gate 	while (len) {
3287*0Sstevel@tonic-gate 		resid = 0;
3288*0Sstevel@tonic-gate 		error = vn_rdwr(UIO_WRITE, vp, buf, len, off,
3289*0Sstevel@tonic-gate 		    UIO_SYSSPACE, FSYNC, rlimit, kcred, &resid);
3290*0Sstevel@tonic-gate 
3291*0Sstevel@tonic-gate 		if (error || resid < 0) {
3292*0Sstevel@tonic-gate 			error = error ? error : EIO;
3293*0Sstevel@tonic-gate 			CACHE_DEBUG((DI_ERR, "write error: %d", error));
3294*0Sstevel@tonic-gate 			break;
3295*0Sstevel@tonic-gate 		}
3296*0Sstevel@tonic-gate 
3297*0Sstevel@tonic-gate 		/*
3298*0Sstevel@tonic-gate 		 * Check if we are making progress
3299*0Sstevel@tonic-gate 		 */
3300*0Sstevel@tonic-gate 		if (resid >= len) {
3301*0Sstevel@tonic-gate 			error = ENOSPC;
3302*0Sstevel@tonic-gate 			break;
3303*0Sstevel@tonic-gate 		}
3304*0Sstevel@tonic-gate 		buf += len - resid;
3305*0Sstevel@tonic-gate 		off += len - resid;
3306*0Sstevel@tonic-gate 		len = resid;
3307*0Sstevel@tonic-gate 	}
3308*0Sstevel@tonic-gate 
3309*0Sstevel@tonic-gate 	return (error);
3310*0Sstevel@tonic-gate }
3311*0Sstevel@tonic-gate 
3312*0Sstevel@tonic-gate extern int modrootloaded;
3313*0Sstevel@tonic-gate 
3314*0Sstevel@tonic-gate static void
3315*0Sstevel@tonic-gate di_cache_write(struct di_cache *cache)
3316*0Sstevel@tonic-gate {
3317*0Sstevel@tonic-gate 	struct di_all	*all;
3318*0Sstevel@tonic-gate 	struct vnode	*vp;
3319*0Sstevel@tonic-gate 	int		oflags;
3320*0Sstevel@tonic-gate 	size_t		map_size;
3321*0Sstevel@tonic-gate 	size_t		chunk;
3322*0Sstevel@tonic-gate 	offset_t	off;
3323*0Sstevel@tonic-gate 	int		error;
3324*0Sstevel@tonic-gate 	char		*buf;
3325*0Sstevel@tonic-gate 
3326*0Sstevel@tonic-gate 	ASSERT(DI_CACHE_LOCKED(*cache));
3327*0Sstevel@tonic-gate 	ASSERT(!servicing_interrupt());
3328*0Sstevel@tonic-gate 
3329*0Sstevel@tonic-gate 	if (cache->cache_size == 0) {
3330*0Sstevel@tonic-gate 		ASSERT(cache->cache_data == NULL);
3331*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "Empty cache. Skipping write"));
3332*0Sstevel@tonic-gate 		return;
3333*0Sstevel@tonic-gate 	}
3334*0Sstevel@tonic-gate 
3335*0Sstevel@tonic-gate 	ASSERT(cache->cache_size > 0);
3336*0Sstevel@tonic-gate 	ASSERT(cache->cache_data);
3337*0Sstevel@tonic-gate 
3338*0Sstevel@tonic-gate 	if (!modrootloaded || rootvp == NULL || vn_is_readonly(rootvp)) {
3339*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "Can't write to rootFS. Skipping write"));
3340*0Sstevel@tonic-gate 		return;
3341*0Sstevel@tonic-gate 	}
3342*0Sstevel@tonic-gate 
3343*0Sstevel@tonic-gate 	all = (struct di_all *)cache->cache_data;
3344*0Sstevel@tonic-gate 
3345*0Sstevel@tonic-gate 	if (!header_plus_one_ok(all)) {
3346*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "Invalid header. Skipping write"));
3347*0Sstevel@tonic-gate 		return;
3348*0Sstevel@tonic-gate 	}
3349*0Sstevel@tonic-gate 
3350*0Sstevel@tonic-gate 	ASSERT(strcmp(all->root_path, "/") == 0);
3351*0Sstevel@tonic-gate 
3352*0Sstevel@tonic-gate 	/*
3353*0Sstevel@tonic-gate 	 * The cache_size is the total allocated memory for the cache.
3354*0Sstevel@tonic-gate 	 * The map_size is the actual size of valid data in the cache.
3355*0Sstevel@tonic-gate 	 * map_size may be smaller than cache_size but cannot exceed
3356*0Sstevel@tonic-gate 	 * cache_size.
3357*0Sstevel@tonic-gate 	 */
3358*0Sstevel@tonic-gate 	if (all->map_size > cache->cache_size) {
3359*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "map_size (0x%x) > cache_size (0x%x)."
3360*0Sstevel@tonic-gate 		    " Skipping write", all->map_size, cache->cache_size));
3361*0Sstevel@tonic-gate 		return;
3362*0Sstevel@tonic-gate 	}
3363*0Sstevel@tonic-gate 
3364*0Sstevel@tonic-gate 	/*
3365*0Sstevel@tonic-gate 	 * First unlink the temp file
3366*0Sstevel@tonic-gate 	 */
3367*0Sstevel@tonic-gate 	error = vn_remove(DI_CACHE_TEMP, UIO_SYSSPACE, RMFILE);
3368*0Sstevel@tonic-gate 	if (error && error != ENOENT) {
3369*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "%s: unlink failed: %d",
3370*0Sstevel@tonic-gate 		    DI_CACHE_TEMP, error));
3371*0Sstevel@tonic-gate 	}
3372*0Sstevel@tonic-gate 
3373*0Sstevel@tonic-gate 	if (error == EROFS) {
3374*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "RDONLY FS. Skipping write"));
3375*0Sstevel@tonic-gate 		return;
3376*0Sstevel@tonic-gate 	}
3377*0Sstevel@tonic-gate 
3378*0Sstevel@tonic-gate 	vp = NULL;
3379*0Sstevel@tonic-gate 	oflags = (FCREAT|FWRITE);
3380*0Sstevel@tonic-gate 	if (error = vn_open(DI_CACHE_TEMP, UIO_SYSSPACE, oflags,
3381*0Sstevel@tonic-gate 	    DI_CACHE_PERMS, &vp, CRCREAT, 0)) {
3382*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "%s: create failed: %d",
3383*0Sstevel@tonic-gate 		    DI_CACHE_TEMP, error));
3384*0Sstevel@tonic-gate 		return;
3385*0Sstevel@tonic-gate 	}
3386*0Sstevel@tonic-gate 
3387*0Sstevel@tonic-gate 	ASSERT(vp);
3388*0Sstevel@tonic-gate 
3389*0Sstevel@tonic-gate 	/*
3390*0Sstevel@tonic-gate 	 * Paranoid: Check if the file is on a read-only FS
3391*0Sstevel@tonic-gate 	 */
3392*0Sstevel@tonic-gate 	if (vn_is_readonly(vp)) {
3393*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "cannot write: readonly FS"));
3394*0Sstevel@tonic-gate 		goto fail;
3395*0Sstevel@tonic-gate 	}
3396*0Sstevel@tonic-gate 
3397*0Sstevel@tonic-gate 	/*
3398*0Sstevel@tonic-gate 	 * Note that we only write map_size bytes to disk - this saves
3399*0Sstevel@tonic-gate 	 * space as the actual cache size may be larger than size of
3400*0Sstevel@tonic-gate 	 * valid data in the cache.
3401*0Sstevel@tonic-gate 	 * Another advantage is that it makes verification of size
3402*0Sstevel@tonic-gate 	 * easier when the file is read later.
3403*0Sstevel@tonic-gate 	 */
3404*0Sstevel@tonic-gate 	map_size = all->map_size;
3405*0Sstevel@tonic-gate 	off = 0;
3406*0Sstevel@tonic-gate 	buf = cache->cache_data;
3407*0Sstevel@tonic-gate 
3408*0Sstevel@tonic-gate 	while (map_size) {
3409*0Sstevel@tonic-gate 		ASSERT(map_size > 0);
3410*0Sstevel@tonic-gate 		/*
3411*0Sstevel@tonic-gate 		 * Write in chunks so that VM system
3412*0Sstevel@tonic-gate 		 * is not overwhelmed
3413*0Sstevel@tonic-gate 		 */
3414*0Sstevel@tonic-gate 		if (map_size > di_chunk * PAGESIZE)
3415*0Sstevel@tonic-gate 			chunk = di_chunk * PAGESIZE;
3416*0Sstevel@tonic-gate 		else
3417*0Sstevel@tonic-gate 			chunk = map_size;
3418*0Sstevel@tonic-gate 
3419*0Sstevel@tonic-gate 		error = chunk_write(vp, off, buf, chunk);
3420*0Sstevel@tonic-gate 		if (error) {
3421*0Sstevel@tonic-gate 			CACHE_DEBUG((DI_ERR, "write failed: off=0x%x: %d",
3422*0Sstevel@tonic-gate 			    off, error));
3423*0Sstevel@tonic-gate 			goto fail;
3424*0Sstevel@tonic-gate 		}
3425*0Sstevel@tonic-gate 
3426*0Sstevel@tonic-gate 		off += chunk;
3427*0Sstevel@tonic-gate 		buf += chunk;
3428*0Sstevel@tonic-gate 		map_size -= chunk;
3429*0Sstevel@tonic-gate 
3430*0Sstevel@tonic-gate 		/* Give pageout a chance to run */
3431*0Sstevel@tonic-gate 		delay(1);
3432*0Sstevel@tonic-gate 	}
3433*0Sstevel@tonic-gate 
3434*0Sstevel@tonic-gate 	/*
3435*0Sstevel@tonic-gate 	 * Now sync the file and close it
3436*0Sstevel@tonic-gate 	 */
3437*0Sstevel@tonic-gate 	if (error = VOP_FSYNC(vp, FSYNC, kcred)) {
3438*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "FSYNC failed: %d", error));
3439*0Sstevel@tonic-gate 	}
3440*0Sstevel@tonic-gate 
3441*0Sstevel@tonic-gate 	if (error = VOP_CLOSE(vp, oflags, 1, (offset_t)0, kcred)) {
3442*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "close() failed: %d", error));
3443*0Sstevel@tonic-gate 		VN_RELE(vp);
3444*0Sstevel@tonic-gate 		return;
3445*0Sstevel@tonic-gate 	}
3446*0Sstevel@tonic-gate 
3447*0Sstevel@tonic-gate 	VN_RELE(vp);
3448*0Sstevel@tonic-gate 
3449*0Sstevel@tonic-gate 	/*
3450*0Sstevel@tonic-gate 	 * Now do the rename
3451*0Sstevel@tonic-gate 	 */
3452*0Sstevel@tonic-gate 	if (error = vn_rename(DI_CACHE_TEMP, DI_CACHE_FILE, UIO_SYSSPACE)) {
3453*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "rename failed: %d", error));
3454*0Sstevel@tonic-gate 		return;
3455*0Sstevel@tonic-gate 	}
3456*0Sstevel@tonic-gate 
3457*0Sstevel@tonic-gate 	CACHE_DEBUG((DI_INFO, "Cache write successful."));
3458*0Sstevel@tonic-gate 
3459*0Sstevel@tonic-gate 	return;
3460*0Sstevel@tonic-gate 
3461*0Sstevel@tonic-gate fail:
3462*0Sstevel@tonic-gate 	(void) VOP_CLOSE(vp, oflags, 1, (offset_t)0, kcred);
3463*0Sstevel@tonic-gate 	VN_RELE(vp);
3464*0Sstevel@tonic-gate }
3465*0Sstevel@tonic-gate 
3466*0Sstevel@tonic-gate 
3467*0Sstevel@tonic-gate /*
3468*0Sstevel@tonic-gate  * Since we could be called early in boot,
3469*0Sstevel@tonic-gate  * use kobj_read_file()
3470*0Sstevel@tonic-gate  */
3471*0Sstevel@tonic-gate static void
3472*0Sstevel@tonic-gate di_cache_read(struct di_cache *cache)
3473*0Sstevel@tonic-gate {
3474*0Sstevel@tonic-gate 	struct _buf	*file;
3475*0Sstevel@tonic-gate 	struct di_all	*all;
3476*0Sstevel@tonic-gate 	int		n;
3477*0Sstevel@tonic-gate 	size_t		map_size, sz, chunk;
3478*0Sstevel@tonic-gate 	offset_t	off;
3479*0Sstevel@tonic-gate 	caddr_t		buf;
3480*0Sstevel@tonic-gate 	uint32_t	saved_crc, crc;
3481*0Sstevel@tonic-gate 
3482*0Sstevel@tonic-gate 	ASSERT(modrootloaded);
3483*0Sstevel@tonic-gate 	ASSERT(DI_CACHE_LOCKED(*cache));
3484*0Sstevel@tonic-gate 	ASSERT(cache->cache_data == NULL);
3485*0Sstevel@tonic-gate 	ASSERT(cache->cache_size == 0);
3486*0Sstevel@tonic-gate 	ASSERT(!servicing_interrupt());
3487*0Sstevel@tonic-gate 
3488*0Sstevel@tonic-gate 	file = kobj_open_file(DI_CACHE_FILE);
3489*0Sstevel@tonic-gate 	if (file == (struct _buf *)-1) {
3490*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "%s: open failed: %d",
3491*0Sstevel@tonic-gate 		    DI_CACHE_FILE, ENOENT));
3492*0Sstevel@tonic-gate 		return;
3493*0Sstevel@tonic-gate 	}
3494*0Sstevel@tonic-gate 
3495*0Sstevel@tonic-gate 	/*
3496*0Sstevel@tonic-gate 	 * Read in the header+root_path first. The root_path must be "/"
3497*0Sstevel@tonic-gate 	 */
3498*0Sstevel@tonic-gate 	all = kmem_zalloc(sizeof (*all) + 1, KM_SLEEP);
3499*0Sstevel@tonic-gate 	n = kobj_read_file(file, (caddr_t)all, sizeof (*all) + 1, 0);
3500*0Sstevel@tonic-gate 
3501*0Sstevel@tonic-gate 	if ((n != sizeof (*all) + 1) || !header_plus_one_ok(all)) {
3502*0Sstevel@tonic-gate 		kmem_free(all, sizeof (*all) + 1);
3503*0Sstevel@tonic-gate 		kobj_close_file(file);
3504*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "cache header: read error or invalid"));
3505*0Sstevel@tonic-gate 		return;
3506*0Sstevel@tonic-gate 	}
3507*0Sstevel@tonic-gate 
3508*0Sstevel@tonic-gate 	map_size = all->map_size;
3509*0Sstevel@tonic-gate 
3510*0Sstevel@tonic-gate 	kmem_free(all, sizeof (*all) + 1);
3511*0Sstevel@tonic-gate 
3512*0Sstevel@tonic-gate 	ASSERT(map_size >= sizeof (*all) + 1);
3513*0Sstevel@tonic-gate 
3514*0Sstevel@tonic-gate 	buf = di_cache.cache_data = kmem_alloc(map_size, KM_SLEEP);
3515*0Sstevel@tonic-gate 	sz = map_size;
3516*0Sstevel@tonic-gate 	off = 0;
3517*0Sstevel@tonic-gate 	while (sz) {
3518*0Sstevel@tonic-gate 		/* Don't overload VM with large reads */
3519*0Sstevel@tonic-gate 		chunk = (sz > di_chunk * PAGESIZE) ? di_chunk * PAGESIZE : sz;
3520*0Sstevel@tonic-gate 		n = kobj_read_file(file, buf, chunk, off);
3521*0Sstevel@tonic-gate 		if (n != chunk) {
3522*0Sstevel@tonic-gate 			CACHE_DEBUG((DI_ERR, "%s: read error at offset: %lld",
3523*0Sstevel@tonic-gate 			    DI_CACHE_FILE, off));
3524*0Sstevel@tonic-gate 			goto fail;
3525*0Sstevel@tonic-gate 		}
3526*0Sstevel@tonic-gate 		off += chunk;
3527*0Sstevel@tonic-gate 		buf += chunk;
3528*0Sstevel@tonic-gate 		sz -= chunk;
3529*0Sstevel@tonic-gate 	}
3530*0Sstevel@tonic-gate 
3531*0Sstevel@tonic-gate 	ASSERT(off == map_size);
3532*0Sstevel@tonic-gate 
3533*0Sstevel@tonic-gate 	/*
3534*0Sstevel@tonic-gate 	 * Read past expected EOF to verify size.
3535*0Sstevel@tonic-gate 	 */
3536*0Sstevel@tonic-gate 	if (kobj_read_file(file, (caddr_t)&sz, 1, off) > 0) {
3537*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "%s: file size changed", DI_CACHE_FILE));
3538*0Sstevel@tonic-gate 		goto fail;
3539*0Sstevel@tonic-gate 	}
3540*0Sstevel@tonic-gate 
3541*0Sstevel@tonic-gate 	all = (struct di_all *)di_cache.cache_data;
3542*0Sstevel@tonic-gate 	if (!header_plus_one_ok(all)) {
3543*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "%s: file header changed", DI_CACHE_FILE));
3544*0Sstevel@tonic-gate 		goto fail;
3545*0Sstevel@tonic-gate 	}
3546*0Sstevel@tonic-gate 
3547*0Sstevel@tonic-gate 	/*
3548*0Sstevel@tonic-gate 	 * Compute CRC with checksum field in the cache data set to 0
3549*0Sstevel@tonic-gate 	 */
3550*0Sstevel@tonic-gate 	saved_crc = all->cache_checksum;
3551*0Sstevel@tonic-gate 	all->cache_checksum = 0;
3552*0Sstevel@tonic-gate 	CRC32(crc, di_cache.cache_data, map_size, -1U, crc32_table);
3553*0Sstevel@tonic-gate 	all->cache_checksum = saved_crc;
3554*0Sstevel@tonic-gate 
3555*0Sstevel@tonic-gate 	if (crc != all->cache_checksum) {
3556*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR,
3557*0Sstevel@tonic-gate 		    "%s: checksum error: expected=0x%x actual=0x%x",
3558*0Sstevel@tonic-gate 		    DI_CACHE_FILE, all->cache_checksum, crc));
3559*0Sstevel@tonic-gate 		goto fail;
3560*0Sstevel@tonic-gate 	}
3561*0Sstevel@tonic-gate 
3562*0Sstevel@tonic-gate 	if (all->map_size != map_size) {
3563*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "%s: map size changed", DI_CACHE_FILE));
3564*0Sstevel@tonic-gate 		goto fail;
3565*0Sstevel@tonic-gate 	}
3566*0Sstevel@tonic-gate 
3567*0Sstevel@tonic-gate 	kobj_close_file(file);
3568*0Sstevel@tonic-gate 
3569*0Sstevel@tonic-gate 	di_cache.cache_size = map_size;
3570*0Sstevel@tonic-gate 
3571*0Sstevel@tonic-gate 	return;
3572*0Sstevel@tonic-gate 
3573*0Sstevel@tonic-gate fail:
3574*0Sstevel@tonic-gate 	kmem_free(di_cache.cache_data, map_size);
3575*0Sstevel@tonic-gate 	kobj_close_file(file);
3576*0Sstevel@tonic-gate 	di_cache.cache_data = NULL;
3577*0Sstevel@tonic-gate 	di_cache.cache_size = 0;
3578*0Sstevel@tonic-gate }
3579*0Sstevel@tonic-gate 
3580*0Sstevel@tonic-gate 
3581*0Sstevel@tonic-gate /*
3582*0Sstevel@tonic-gate  * Checks if arguments are valid for using the cache.
3583*0Sstevel@tonic-gate  */
3584*0Sstevel@tonic-gate static int
3585*0Sstevel@tonic-gate cache_args_valid(struct di_state *st, int *error)
3586*0Sstevel@tonic-gate {
3587*0Sstevel@tonic-gate 	ASSERT(error);
3588*0Sstevel@tonic-gate 	ASSERT(st->mem_size > 0);
3589*0Sstevel@tonic-gate 	ASSERT(st->memlist != NULL);
3590*0Sstevel@tonic-gate 
3591*0Sstevel@tonic-gate 	if (!modrootloaded || !i_ddi_io_initialized()) {
3592*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR,
3593*0Sstevel@tonic-gate 		    "cache lookup failure: I/O subsystem not inited"));
3594*0Sstevel@tonic-gate 		*error = ENOTACTIVE;
3595*0Sstevel@tonic-gate 		return (0);
3596*0Sstevel@tonic-gate 	}
3597*0Sstevel@tonic-gate 
3598*0Sstevel@tonic-gate 	/*
3599*0Sstevel@tonic-gate 	 * No other flags allowed with DINFOCACHE
3600*0Sstevel@tonic-gate 	 */
3601*0Sstevel@tonic-gate 	if (st->command != (DINFOCACHE & DIIOC_MASK)) {
3602*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR,
3603*0Sstevel@tonic-gate 		    "cache lookup failure: bad flags: 0x%x",
3604*0Sstevel@tonic-gate 		    st->command));
3605*0Sstevel@tonic-gate 		*error = EINVAL;
3606*0Sstevel@tonic-gate 		return (0);
3607*0Sstevel@tonic-gate 	}
3608*0Sstevel@tonic-gate 
3609*0Sstevel@tonic-gate 	if (strcmp(DI_ALL_PTR(st)->root_path, "/") != 0) {
3610*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR,
3611*0Sstevel@tonic-gate 		    "cache lookup failure: bad root: %s",
3612*0Sstevel@tonic-gate 		    DI_ALL_PTR(st)->root_path));
3613*0Sstevel@tonic-gate 		*error = EINVAL;
3614*0Sstevel@tonic-gate 		return (0);
3615*0Sstevel@tonic-gate 	}
3616*0Sstevel@tonic-gate 
3617*0Sstevel@tonic-gate 	CACHE_DEBUG((DI_INFO, "cache lookup args ok: 0x%x", st->command));
3618*0Sstevel@tonic-gate 
3619*0Sstevel@tonic-gate 	*error = 0;
3620*0Sstevel@tonic-gate 
3621*0Sstevel@tonic-gate 	return (1);
3622*0Sstevel@tonic-gate }
3623*0Sstevel@tonic-gate 
3624*0Sstevel@tonic-gate static int
3625*0Sstevel@tonic-gate snapshot_is_cacheable(struct di_state *st)
3626*0Sstevel@tonic-gate {
3627*0Sstevel@tonic-gate 	ASSERT(st->mem_size > 0);
3628*0Sstevel@tonic-gate 	ASSERT(st->memlist != NULL);
3629*0Sstevel@tonic-gate 
3630*0Sstevel@tonic-gate 	if (st->command != (DI_CACHE_SNAPSHOT_FLAGS & DIIOC_MASK)) {
3631*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_INFO,
3632*0Sstevel@tonic-gate 		    "not cacheable: incompatible flags: 0x%x",
3633*0Sstevel@tonic-gate 		    st->command));
3634*0Sstevel@tonic-gate 		return (0);
3635*0Sstevel@tonic-gate 	}
3636*0Sstevel@tonic-gate 
3637*0Sstevel@tonic-gate 	if (strcmp(DI_ALL_PTR(st)->root_path, "/") != 0) {
3638*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_INFO,
3639*0Sstevel@tonic-gate 		    "not cacheable: incompatible root path: %s",
3640*0Sstevel@tonic-gate 		    DI_ALL_PTR(st)->root_path));
3641*0Sstevel@tonic-gate 		return (0);
3642*0Sstevel@tonic-gate 	}
3643*0Sstevel@tonic-gate 
3644*0Sstevel@tonic-gate 	CACHE_DEBUG((DI_INFO, "cacheable snapshot request: 0x%x", st->command));
3645*0Sstevel@tonic-gate 
3646*0Sstevel@tonic-gate 	return (1);
3647*0Sstevel@tonic-gate }
3648*0Sstevel@tonic-gate 
3649*0Sstevel@tonic-gate static int
3650*0Sstevel@tonic-gate di_cache_lookup(struct di_state *st)
3651*0Sstevel@tonic-gate {
3652*0Sstevel@tonic-gate 	size_t	rval;
3653*0Sstevel@tonic-gate 	int	cache_valid;
3654*0Sstevel@tonic-gate 
3655*0Sstevel@tonic-gate 	ASSERT(cache_args_valid(st, &cache_valid));
3656*0Sstevel@tonic-gate 	ASSERT(modrootloaded);
3657*0Sstevel@tonic-gate 
3658*0Sstevel@tonic-gate 	DI_CACHE_LOCK(di_cache);
3659*0Sstevel@tonic-gate 
3660*0Sstevel@tonic-gate 	/*
3661*0Sstevel@tonic-gate 	 * The following assignment determines the validity
3662*0Sstevel@tonic-gate 	 * of the cache as far as this snapshot is concerned.
3663*0Sstevel@tonic-gate 	 */
3664*0Sstevel@tonic-gate 	cache_valid = di_cache.cache_valid;
3665*0Sstevel@tonic-gate 
3666*0Sstevel@tonic-gate 	if (cache_valid && di_cache.cache_data == NULL) {
3667*0Sstevel@tonic-gate 		di_cache_read(&di_cache);
3668*0Sstevel@tonic-gate 		/* check for read or file error */
3669*0Sstevel@tonic-gate 		if (di_cache.cache_data == NULL)
3670*0Sstevel@tonic-gate 			cache_valid = 0;
3671*0Sstevel@tonic-gate 	}
3672*0Sstevel@tonic-gate 
3673*0Sstevel@tonic-gate 	if (cache_valid) {
3674*0Sstevel@tonic-gate 		/*
3675*0Sstevel@tonic-gate 		 * Ok, the cache was valid as of this particular
3676*0Sstevel@tonic-gate 		 * snapshot. Copy the cached snapshot. This is safe
3677*0Sstevel@tonic-gate 		 * to do as the cache cannot be freed (we hold the
3678*0Sstevel@tonic-gate 		 * cache lock). Free the memory allocated in di_state
3679*0Sstevel@tonic-gate 		 * up until this point - we will simply copy everything
3680*0Sstevel@tonic-gate 		 * in the cache.
3681*0Sstevel@tonic-gate 		 */
3682*0Sstevel@tonic-gate 
3683*0Sstevel@tonic-gate 		ASSERT(di_cache.cache_data != NULL);
3684*0Sstevel@tonic-gate 		ASSERT(di_cache.cache_size > 0);
3685*0Sstevel@tonic-gate 
3686*0Sstevel@tonic-gate 		di_freemem(st);
3687*0Sstevel@tonic-gate 
3688*0Sstevel@tonic-gate 		rval = 0;
3689*0Sstevel@tonic-gate 		if (di_cache2mem(&di_cache, st) > 0) {
3690*0Sstevel@tonic-gate 
3691*0Sstevel@tonic-gate 			ASSERT(DI_ALL_PTR(st));
3692*0Sstevel@tonic-gate 
3693*0Sstevel@tonic-gate 			/*
3694*0Sstevel@tonic-gate 			 * map_size is size of valid data in the
3695*0Sstevel@tonic-gate 			 * cached snapshot and may be less than
3696*0Sstevel@tonic-gate 			 * size of the cache.
3697*0Sstevel@tonic-gate 			 */
3698*0Sstevel@tonic-gate 			rval = DI_ALL_PTR(st)->map_size;
3699*0Sstevel@tonic-gate 
3700*0Sstevel@tonic-gate 			ASSERT(rval >= sizeof (struct di_all));
3701*0Sstevel@tonic-gate 			ASSERT(rval <= di_cache.cache_size);
3702*0Sstevel@tonic-gate 		}
3703*0Sstevel@tonic-gate 	} else {
3704*0Sstevel@tonic-gate 		/*
3705*0Sstevel@tonic-gate 		 * The cache isn't valid, we need to take a snapshot.
3706*0Sstevel@tonic-gate 		 * Set the command flags appropriately
3707*0Sstevel@tonic-gate 		 */
3708*0Sstevel@tonic-gate 		ASSERT(st->command == (DINFOCACHE & DIIOC_MASK));
3709*0Sstevel@tonic-gate 		st->command = (DI_CACHE_SNAPSHOT_FLAGS & DIIOC_MASK);
3710*0Sstevel@tonic-gate 		rval = di_cache_update(st);
3711*0Sstevel@tonic-gate 		st->command = (DINFOCACHE & DIIOC_MASK);
3712*0Sstevel@tonic-gate 	}
3713*0Sstevel@tonic-gate 
3714*0Sstevel@tonic-gate 	DI_CACHE_UNLOCK(di_cache);
3715*0Sstevel@tonic-gate 
3716*0Sstevel@tonic-gate 	/*
3717*0Sstevel@tonic-gate 	 * For cached snapshots, the devinfo driver always returns
3718*0Sstevel@tonic-gate 	 * a snapshot rooted at "/".
3719*0Sstevel@tonic-gate 	 */
3720*0Sstevel@tonic-gate 	ASSERT(rval == 0 || strcmp(DI_ALL_PTR(st)->root_path, "/") == 0);
3721*0Sstevel@tonic-gate 
3722*0Sstevel@tonic-gate 	return (rval);
3723*0Sstevel@tonic-gate }
3724*0Sstevel@tonic-gate 
3725*0Sstevel@tonic-gate /*
3726*0Sstevel@tonic-gate  * This is a forced update of the cache  - the previous state of the cache
3727*0Sstevel@tonic-gate  * may be:
3728*0Sstevel@tonic-gate  *	- unpopulated
3729*0Sstevel@tonic-gate  *	- populated and invalid
3730*0Sstevel@tonic-gate  *	- populated and valid
3731*0Sstevel@tonic-gate  */
3732*0Sstevel@tonic-gate static int
3733*0Sstevel@tonic-gate di_cache_update(struct di_state *st)
3734*0Sstevel@tonic-gate {
3735*0Sstevel@tonic-gate 	int rval;
3736*0Sstevel@tonic-gate 	uint32_t crc;
3737*0Sstevel@tonic-gate 	struct di_all *all;
3738*0Sstevel@tonic-gate 
3739*0Sstevel@tonic-gate 	ASSERT(DI_CACHE_LOCKED(di_cache));
3740*0Sstevel@tonic-gate 	ASSERT(snapshot_is_cacheable(st));
3741*0Sstevel@tonic-gate 
3742*0Sstevel@tonic-gate 	/*
3743*0Sstevel@tonic-gate 	 * Free the in-core cache and the on-disk file (if they exist)
3744*0Sstevel@tonic-gate 	 */
3745*0Sstevel@tonic-gate 	i_ddi_di_cache_free(&di_cache);
3746*0Sstevel@tonic-gate 
3747*0Sstevel@tonic-gate 	/*
3748*0Sstevel@tonic-gate 	 * Set valid flag before taking the snapshot,
3749*0Sstevel@tonic-gate 	 * so that any invalidations that arrive
3750*0Sstevel@tonic-gate 	 * during or after the snapshot are not
3751*0Sstevel@tonic-gate 	 * removed by us.
3752*0Sstevel@tonic-gate 	 */
3753*0Sstevel@tonic-gate 	atomic_or_32(&di_cache.cache_valid, 1);
3754*0Sstevel@tonic-gate 
3755*0Sstevel@tonic-gate 	modunload_disable();
3756*0Sstevel@tonic-gate 	rval = di_snapshot(st);
3757*0Sstevel@tonic-gate 	modunload_enable();
3758*0Sstevel@tonic-gate 
3759*0Sstevel@tonic-gate 	if (rval == 0) {
3760*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "can't update cache: bad snapshot"));
3761*0Sstevel@tonic-gate 		return (0);
3762*0Sstevel@tonic-gate 	}
3763*0Sstevel@tonic-gate 
3764*0Sstevel@tonic-gate 	DI_ALL_PTR(st)->map_size = rval;
3765*0Sstevel@tonic-gate 
3766*0Sstevel@tonic-gate 	if (di_mem2cache(st, &di_cache) == 0) {
3767*0Sstevel@tonic-gate 		CACHE_DEBUG((DI_ERR, "can't update cache: copy failed"));
3768*0Sstevel@tonic-gate 		return (0);
3769*0Sstevel@tonic-gate 	}
3770*0Sstevel@tonic-gate 
3771*0Sstevel@tonic-gate 	ASSERT(di_cache.cache_data);
3772*0Sstevel@tonic-gate 	ASSERT(di_cache.cache_size > 0);
3773*0Sstevel@tonic-gate 
3774*0Sstevel@tonic-gate 	/*
3775*0Sstevel@tonic-gate 	 * Now that we have cached the snapshot, compute its checksum.
3776*0Sstevel@tonic-gate 	 * The checksum is only computed over the valid data in the
3777*0Sstevel@tonic-gate 	 * cache, not the entire cache.
3778*0Sstevel@tonic-gate 	 * Also, set all the fields (except checksum) before computing
3779*0Sstevel@tonic-gate 	 * checksum.
3780*0Sstevel@tonic-gate 	 */
3781*0Sstevel@tonic-gate 	all = (struct di_all *)di_cache.cache_data;
3782*0Sstevel@tonic-gate 	all->cache_magic = DI_CACHE_MAGIC;
3783*0Sstevel@tonic-gate 	all->map_size = rval;
3784*0Sstevel@tonic-gate 
3785*0Sstevel@tonic-gate 	ASSERT(all->cache_checksum == 0);
3786*0Sstevel@tonic-gate 	CRC32(crc, di_cache.cache_data, all->map_size, -1U, crc32_table);
3787*0Sstevel@tonic-gate 	all->cache_checksum = crc;
3788*0Sstevel@tonic-gate 
3789*0Sstevel@tonic-gate 	di_cache_write(&di_cache);
3790*0Sstevel@tonic-gate 
3791*0Sstevel@tonic-gate 	return (rval);
3792*0Sstevel@tonic-gate }
3793*0Sstevel@tonic-gate 
3794*0Sstevel@tonic-gate static void
3795*0Sstevel@tonic-gate di_cache_print(di_cache_debug_t msglevel, char *fmt, ...)
3796*0Sstevel@tonic-gate {
3797*0Sstevel@tonic-gate 	va_list	ap;
3798*0Sstevel@tonic-gate 
3799*0Sstevel@tonic-gate 	if (di_cache_debug <= DI_QUIET)
3800*0Sstevel@tonic-gate 		return;
3801*0Sstevel@tonic-gate 
3802*0Sstevel@tonic-gate 	if (di_cache_debug < msglevel)
3803*0Sstevel@tonic-gate 		return;
3804*0Sstevel@tonic-gate 
3805*0Sstevel@tonic-gate 	switch (msglevel) {
3806*0Sstevel@tonic-gate 		case DI_ERR:
3807*0Sstevel@tonic-gate 			msglevel = CE_WARN;
3808*0Sstevel@tonic-gate 			break;
3809*0Sstevel@tonic-gate 		case DI_INFO:
3810*0Sstevel@tonic-gate 		case DI_TRACE:
3811*0Sstevel@tonic-gate 		default:
3812*0Sstevel@tonic-gate 			msglevel = CE_NOTE;
3813*0Sstevel@tonic-gate 			break;
3814*0Sstevel@tonic-gate 	}
3815*0Sstevel@tonic-gate 
3816*0Sstevel@tonic-gate 	va_start(ap, fmt);
3817*0Sstevel@tonic-gate 	vcmn_err(msglevel, fmt, ap);
3818*0Sstevel@tonic-gate 	va_end(ap);
3819*0Sstevel@tonic-gate }
3820