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