xref: /titanic_44/usr/src/uts/common/io/ramdisk.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  * Ramdisk device driver.
31*7c478bd9Sstevel@tonic-gate  *
32*7c478bd9Sstevel@tonic-gate  * There are two types of ramdisk: 'real' OBP-created ramdisks, and 'pseudo'
33*7c478bd9Sstevel@tonic-gate  * ramdisks created at runtime with no corresponding OBP device node.  The
34*7c478bd9Sstevel@tonic-gate  * ramdisk(7D) driver is capable of dealing with both, and with the creation
35*7c478bd9Sstevel@tonic-gate  * and deletion of 'pseudo' ramdisks.
36*7c478bd9Sstevel@tonic-gate  *
37*7c478bd9Sstevel@tonic-gate  * Every ramdisk has a single 'state' structure which maintains data for
38*7c478bd9Sstevel@tonic-gate  * that ramdisk, and is assigned a single minor number.  The bottom 10-bits
39*7c478bd9Sstevel@tonic-gate  * of the minor number index the state structures; the top 8-bits give a
40*7c478bd9Sstevel@tonic-gate  * 'real OBP disk' number, i.e. they are zero for 'pseudo' ramdisks.  Thus
41*7c478bd9Sstevel@tonic-gate  * it is possible to distinguish 'real' from 'pseudo' ramdisks using the
42*7c478bd9Sstevel@tonic-gate  * top 8-bits of the minor number.
43*7c478bd9Sstevel@tonic-gate  *
44*7c478bd9Sstevel@tonic-gate  * Each OBP-created ramdisk has its own node in the device tree with an
45*7c478bd9Sstevel@tonic-gate  * "existing" property which describes the one-or-more physical address ranges
46*7c478bd9Sstevel@tonic-gate  * assigned to the ramdisk.  All 'pseudo' ramdisks share a common devinfo
47*7c478bd9Sstevel@tonic-gate  * structure.
48*7c478bd9Sstevel@tonic-gate  *
49*7c478bd9Sstevel@tonic-gate  * A single character device node is used by ramdiskadm(1M) to communicate
50*7c478bd9Sstevel@tonic-gate  * with the ramdisk driver, with minor number 0:
51*7c478bd9Sstevel@tonic-gate  *
52*7c478bd9Sstevel@tonic-gate  *	/dev/ramdiskctl -> /devices/pseudo/ramdisk@0:ctl
53*7c478bd9Sstevel@tonic-gate  *
54*7c478bd9Sstevel@tonic-gate  * For consistent access, block and raw device nodes are created for *every*
55*7c478bd9Sstevel@tonic-gate  * ramdisk.  For 'pseudo' ramdisks:
56*7c478bd9Sstevel@tonic-gate  *
57*7c478bd9Sstevel@tonic-gate  *	/dev/ramdisk/<diskname>  -> /devices/pseudo/ramdisk@0:<diskname>
58*7c478bd9Sstevel@tonic-gate  *	/dev/rramdisk/<diskname> -> /devices/pseudo/ramdisk@0:<diskname>,raw
59*7c478bd9Sstevel@tonic-gate  *
60*7c478bd9Sstevel@tonic-gate  * For OBP-created ramdisks:
61*7c478bd9Sstevel@tonic-gate  *
62*7c478bd9Sstevel@tonic-gate  *	/dev/ramdisk/<diskname>  -> /devices/ramdisk-<diskname>:a
63*7c478bd9Sstevel@tonic-gate  *	/dev/ramdisk/<diskname>  -> /devices/ramdisk-<diskname>:a,raw
64*7c478bd9Sstevel@tonic-gate  *
65*7c478bd9Sstevel@tonic-gate  * This allows the transition from the standalone to the kernel to proceed
66*7c478bd9Sstevel@tonic-gate  * when booting from a ramdisk, and for the installation to correctly identify
67*7c478bd9Sstevel@tonic-gate  * the root device.
68*7c478bd9Sstevel@tonic-gate  */
69*7c478bd9Sstevel@tonic-gate 
70*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
71*7c478bd9Sstevel@tonic-gate #include <sys/param.h>
72*7c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
73*7c478bd9Sstevel@tonic-gate #include <sys/errno.h>
74*7c478bd9Sstevel@tonic-gate #include <sys/uio.h>
75*7c478bd9Sstevel@tonic-gate #include <sys/buf.h>
76*7c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
77*7c478bd9Sstevel@tonic-gate #include <sys/open.h>
78*7c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
79*7c478bd9Sstevel@tonic-gate #include <sys/poll.h>
80*7c478bd9Sstevel@tonic-gate #include <sys/conf.h>
81*7c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
82*7c478bd9Sstevel@tonic-gate #include <sys/stat.h>
83*7c478bd9Sstevel@tonic-gate #include <sys/file.h>
84*7c478bd9Sstevel@tonic-gate #include <sys/vol.h>
85*7c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
86*7c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
87*7c478bd9Sstevel@tonic-gate #include <sys/ramdisk.h>
88*7c478bd9Sstevel@tonic-gate #include <vm/seg_kmem.h>
89*7c478bd9Sstevel@tonic-gate 
90*7c478bd9Sstevel@tonic-gate /*
91*7c478bd9Sstevel@tonic-gate  * An opaque handle where information about our set of ramdisk devices lives.
92*7c478bd9Sstevel@tonic-gate  */
93*7c478bd9Sstevel@tonic-gate static void	*rd_statep;
94*7c478bd9Sstevel@tonic-gate 
95*7c478bd9Sstevel@tonic-gate /*
96*7c478bd9Sstevel@tonic-gate  * Pointer to devinfo for the 'pseudo' ramdisks.  Real OBP-created ramdisks
97*7c478bd9Sstevel@tonic-gate  * get their own individual devinfo.
98*7c478bd9Sstevel@tonic-gate  */
99*7c478bd9Sstevel@tonic-gate static dev_info_t *rd_dip = NULL;
100*7c478bd9Sstevel@tonic-gate 
101*7c478bd9Sstevel@tonic-gate /*
102*7c478bd9Sstevel@tonic-gate  * Global state lock.
103*7c478bd9Sstevel@tonic-gate  */
104*7c478bd9Sstevel@tonic-gate static kmutex_t	rd_lock;
105*7c478bd9Sstevel@tonic-gate 
106*7c478bd9Sstevel@tonic-gate /*
107*7c478bd9Sstevel@tonic-gate  * Maximum number of ramdisks supported by this driver.
108*7c478bd9Sstevel@tonic-gate  */
109*7c478bd9Sstevel@tonic-gate static uint32_t	rd_max_disks = RD_DFLT_DISKS;
110*7c478bd9Sstevel@tonic-gate 
111*7c478bd9Sstevel@tonic-gate /*
112*7c478bd9Sstevel@tonic-gate  * Percentage of physical memory which can be assigned to pseudo ramdisks,
113*7c478bd9Sstevel@tonic-gate  * what that equates to in pages, and how many pages are currently assigned.
114*7c478bd9Sstevel@tonic-gate  */
115*7c478bd9Sstevel@tonic-gate static uint_t	rd_percent_physmem = RD_DEFAULT_PERCENT_PHYSMEM;
116*7c478bd9Sstevel@tonic-gate static pgcnt_t	rd_max_physmem;
117*7c478bd9Sstevel@tonic-gate static pgcnt_t	rd_tot_physmem;
118*7c478bd9Sstevel@tonic-gate 
119*7c478bd9Sstevel@tonic-gate static uint_t	rd_maxphys = RD_DEFAULT_MAXPHYS;
120*7c478bd9Sstevel@tonic-gate 
121*7c478bd9Sstevel@tonic-gate /*
122*7c478bd9Sstevel@tonic-gate  * Is the driver busy, i.e. are there any pseudo ramdisk devices in existence?
123*7c478bd9Sstevel@tonic-gate  */
124*7c478bd9Sstevel@tonic-gate static int
125*7c478bd9Sstevel@tonic-gate rd_is_busy(void)
126*7c478bd9Sstevel@tonic-gate {
127*7c478bd9Sstevel@tonic-gate 	minor_t	minor;
128*7c478bd9Sstevel@tonic-gate 	rd_devstate_t	*rsp;
129*7c478bd9Sstevel@tonic-gate 
130*7c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&rd_lock));
131*7c478bd9Sstevel@tonic-gate 	for (minor = 1; minor <= rd_max_disks; ++minor) {
132*7c478bd9Sstevel@tonic-gate 		if ((rsp = ddi_get_soft_state(rd_statep, minor)) != NULL &&
133*7c478bd9Sstevel@tonic-gate 		    rsp->rd_dip == rd_dip) {
134*7c478bd9Sstevel@tonic-gate 			return (EBUSY);
135*7c478bd9Sstevel@tonic-gate 		}
136*7c478bd9Sstevel@tonic-gate 	}
137*7c478bd9Sstevel@tonic-gate 	return (0);
138*7c478bd9Sstevel@tonic-gate }
139*7c478bd9Sstevel@tonic-gate 
140*7c478bd9Sstevel@tonic-gate /*
141*7c478bd9Sstevel@tonic-gate  * Find the first free minor number; returns zero if there isn't one.
142*7c478bd9Sstevel@tonic-gate  */
143*7c478bd9Sstevel@tonic-gate static minor_t
144*7c478bd9Sstevel@tonic-gate rd_find_free_minor(void)
145*7c478bd9Sstevel@tonic-gate {
146*7c478bd9Sstevel@tonic-gate 	minor_t	minor;
147*7c478bd9Sstevel@tonic-gate 
148*7c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&rd_lock));
149*7c478bd9Sstevel@tonic-gate 	for (minor = 1; minor <= rd_max_disks; ++minor) {
150*7c478bd9Sstevel@tonic-gate 		if (ddi_get_soft_state(rd_statep, minor) == NULL) {
151*7c478bd9Sstevel@tonic-gate 			return (minor);
152*7c478bd9Sstevel@tonic-gate 		}
153*7c478bd9Sstevel@tonic-gate 	}
154*7c478bd9Sstevel@tonic-gate 	return (0);
155*7c478bd9Sstevel@tonic-gate }
156*7c478bd9Sstevel@tonic-gate 
157*7c478bd9Sstevel@tonic-gate /*
158*7c478bd9Sstevel@tonic-gate  * Locate the rd_devstate for the named ramdisk; returns NULL if not found.
159*7c478bd9Sstevel@tonic-gate  * Each ramdisk is identified uniquely by name, i.e. an OBP-created ramdisk
160*7c478bd9Sstevel@tonic-gate  * cannot have the same name as a pseudo ramdisk.
161*7c478bd9Sstevel@tonic-gate  */
162*7c478bd9Sstevel@tonic-gate static rd_devstate_t *
163*7c478bd9Sstevel@tonic-gate rd_find_named_disk(char *name)
164*7c478bd9Sstevel@tonic-gate {
165*7c478bd9Sstevel@tonic-gate 	minor_t		minor;
166*7c478bd9Sstevel@tonic-gate 	rd_devstate_t	*rsp;
167*7c478bd9Sstevel@tonic-gate 
168*7c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&rd_lock));
169*7c478bd9Sstevel@tonic-gate 	for (minor = 1; minor <= rd_max_disks; ++minor) {
170*7c478bd9Sstevel@tonic-gate 		if ((rsp = ddi_get_soft_state(rd_statep, minor)) != NULL &&
171*7c478bd9Sstevel@tonic-gate 		    strcmp(rsp->rd_name, name) == 0) {
172*7c478bd9Sstevel@tonic-gate 			return (rsp);
173*7c478bd9Sstevel@tonic-gate 		}
174*7c478bd9Sstevel@tonic-gate 	}
175*7c478bd9Sstevel@tonic-gate 	return (NULL);
176*7c478bd9Sstevel@tonic-gate }
177*7c478bd9Sstevel@tonic-gate 
178*7c478bd9Sstevel@tonic-gate /*
179*7c478bd9Sstevel@tonic-gate  * Locate the rd_devstate for the real OBP-created ramdisk whose devinfo
180*7c478bd9Sstevel@tonic-gate  * is referenced by 'dip'; returns NULL if not found (shouldn't happen).
181*7c478bd9Sstevel@tonic-gate  */
182*7c478bd9Sstevel@tonic-gate static rd_devstate_t *
183*7c478bd9Sstevel@tonic-gate rd_find_dip_state(dev_info_t *dip)
184*7c478bd9Sstevel@tonic-gate {
185*7c478bd9Sstevel@tonic-gate 	minor_t		minor;
186*7c478bd9Sstevel@tonic-gate 	rd_devstate_t	*rsp;
187*7c478bd9Sstevel@tonic-gate 
188*7c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&rd_lock));
189*7c478bd9Sstevel@tonic-gate 	for (minor = 1; minor <= rd_max_disks; ++minor) {
190*7c478bd9Sstevel@tonic-gate 		if ((rsp = ddi_get_soft_state(rd_statep, minor)) != NULL &&
191*7c478bd9Sstevel@tonic-gate 		    rsp->rd_dip == dip) {
192*7c478bd9Sstevel@tonic-gate 			return (rsp);
193*7c478bd9Sstevel@tonic-gate 		}
194*7c478bd9Sstevel@tonic-gate 	}
195*7c478bd9Sstevel@tonic-gate 	return (NULL);
196*7c478bd9Sstevel@tonic-gate }
197*7c478bd9Sstevel@tonic-gate 
198*7c478bd9Sstevel@tonic-gate /*
199*7c478bd9Sstevel@tonic-gate  * Is the ramdisk open?
200*7c478bd9Sstevel@tonic-gate  */
201*7c478bd9Sstevel@tonic-gate static int
202*7c478bd9Sstevel@tonic-gate rd_is_open(rd_devstate_t *rsp)
203*7c478bd9Sstevel@tonic-gate {
204*7c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&rd_lock));
205*7c478bd9Sstevel@tonic-gate 	return (rsp->rd_chr_open || rsp->rd_blk_open || rsp->rd_lyr_open_cnt);
206*7c478bd9Sstevel@tonic-gate }
207*7c478bd9Sstevel@tonic-gate 
208*7c478bd9Sstevel@tonic-gate /*
209*7c478bd9Sstevel@tonic-gate  * Mark the ramdisk open.
210*7c478bd9Sstevel@tonic-gate  */
211*7c478bd9Sstevel@tonic-gate static int
212*7c478bd9Sstevel@tonic-gate rd_opened(rd_devstate_t *rsp, int otyp)
213*7c478bd9Sstevel@tonic-gate {
214*7c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&rd_lock));
215*7c478bd9Sstevel@tonic-gate 	switch (otyp) {
216*7c478bd9Sstevel@tonic-gate 	case OTYP_CHR:
217*7c478bd9Sstevel@tonic-gate 		rsp->rd_chr_open = 1;
218*7c478bd9Sstevel@tonic-gate 		break;
219*7c478bd9Sstevel@tonic-gate 	case OTYP_BLK:
220*7c478bd9Sstevel@tonic-gate 		rsp->rd_blk_open = 1;
221*7c478bd9Sstevel@tonic-gate 		break;
222*7c478bd9Sstevel@tonic-gate 	case OTYP_LYR:
223*7c478bd9Sstevel@tonic-gate 		rsp->rd_lyr_open_cnt++;
224*7c478bd9Sstevel@tonic-gate 		break;
225*7c478bd9Sstevel@tonic-gate 	default:
226*7c478bd9Sstevel@tonic-gate 		return (-1);
227*7c478bd9Sstevel@tonic-gate 	}
228*7c478bd9Sstevel@tonic-gate 	return (0);
229*7c478bd9Sstevel@tonic-gate }
230*7c478bd9Sstevel@tonic-gate 
231*7c478bd9Sstevel@tonic-gate /*
232*7c478bd9Sstevel@tonic-gate  * Mark the ramdisk closed.
233*7c478bd9Sstevel@tonic-gate  */
234*7c478bd9Sstevel@tonic-gate static void
235*7c478bd9Sstevel@tonic-gate rd_closed(rd_devstate_t *rsp, int otyp)
236*7c478bd9Sstevel@tonic-gate {
237*7c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&rd_lock));
238*7c478bd9Sstevel@tonic-gate 	switch (otyp) {
239*7c478bd9Sstevel@tonic-gate 	case OTYP_CHR:
240*7c478bd9Sstevel@tonic-gate 		rsp->rd_chr_open = 0;
241*7c478bd9Sstevel@tonic-gate 		break;
242*7c478bd9Sstevel@tonic-gate 	case OTYP_BLK:
243*7c478bd9Sstevel@tonic-gate 		rsp->rd_blk_open = 0;
244*7c478bd9Sstevel@tonic-gate 		break;
245*7c478bd9Sstevel@tonic-gate 	case OTYP_LYR:
246*7c478bd9Sstevel@tonic-gate 		rsp->rd_lyr_open_cnt--;
247*7c478bd9Sstevel@tonic-gate 		break;
248*7c478bd9Sstevel@tonic-gate 	default:
249*7c478bd9Sstevel@tonic-gate 		break;
250*7c478bd9Sstevel@tonic-gate 	}
251*7c478bd9Sstevel@tonic-gate }
252*7c478bd9Sstevel@tonic-gate 
253*7c478bd9Sstevel@tonic-gate static void
254*7c478bd9Sstevel@tonic-gate rd_init_tuneables(void)
255*7c478bd9Sstevel@tonic-gate {
256*7c478bd9Sstevel@tonic-gate 	char	*prop, *p;
257*7c478bd9Sstevel@tonic-gate 
258*7c478bd9Sstevel@tonic-gate 	/*
259*7c478bd9Sstevel@tonic-gate 	 * Ensure sanity of 'rd_max_disks', which may be tuned in ramdisk.conf.
260*7c478bd9Sstevel@tonic-gate 	 */
261*7c478bd9Sstevel@tonic-gate 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, rd_dip, 0,
262*7c478bd9Sstevel@tonic-gate 	    "max_disks", &prop) == DDI_PROP_SUCCESS) {
263*7c478bd9Sstevel@tonic-gate 		p = prop;
264*7c478bd9Sstevel@tonic-gate 		rd_max_disks = (uint32_t)stoi(&p);
265*7c478bd9Sstevel@tonic-gate 		ddi_prop_free(prop);
266*7c478bd9Sstevel@tonic-gate 	}
267*7c478bd9Sstevel@tonic-gate 	if (rd_max_disks >= RD_MAX_DISKS) {
268*7c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "ramdisk: rd_max_disks (%u) too big;"
269*7c478bd9Sstevel@tonic-gate 		    " using default (%u).", rd_max_disks, RD_MAX_DISKS - 1);
270*7c478bd9Sstevel@tonic-gate 
271*7c478bd9Sstevel@tonic-gate 		rd_max_disks = RD_MAX_DISKS - 1;
272*7c478bd9Sstevel@tonic-gate 	}
273*7c478bd9Sstevel@tonic-gate 
274*7c478bd9Sstevel@tonic-gate 	/*
275*7c478bd9Sstevel@tonic-gate 	 * Ensure sanity of 'rd_percent_physmem', which may be tuned
276*7c478bd9Sstevel@tonic-gate 	 * in ramdisk.conf.
277*7c478bd9Sstevel@tonic-gate 	 */
278*7c478bd9Sstevel@tonic-gate 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, rd_dip, 0,
279*7c478bd9Sstevel@tonic-gate 	    "percent_physmem", &prop) == DDI_PROP_SUCCESS) {
280*7c478bd9Sstevel@tonic-gate 		p = prop;
281*7c478bd9Sstevel@tonic-gate 		rd_percent_physmem = (uint_t)stoi(&p);
282*7c478bd9Sstevel@tonic-gate 		ddi_prop_free(prop);
283*7c478bd9Sstevel@tonic-gate 	}
284*7c478bd9Sstevel@tonic-gate 	if (rd_percent_physmem >= 100) {
285*7c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "ramdisk: rd_percent_physmem (%u) >= 100;"
286*7c478bd9Sstevel@tonic-gate 		    " using default (%u%%).", rd_percent_physmem,
287*7c478bd9Sstevel@tonic-gate 		    RD_DEFAULT_PERCENT_PHYSMEM);
288*7c478bd9Sstevel@tonic-gate 
289*7c478bd9Sstevel@tonic-gate 		rd_percent_physmem = RD_DEFAULT_PERCENT_PHYSMEM;
290*7c478bd9Sstevel@tonic-gate 	}
291*7c478bd9Sstevel@tonic-gate 
292*7c478bd9Sstevel@tonic-gate 	/*
293*7c478bd9Sstevel@tonic-gate 	 * Since availrmem is in pages (and is a long), this won't overflow.
294*7c478bd9Sstevel@tonic-gate 	 */
295*7c478bd9Sstevel@tonic-gate 	rd_max_physmem = (availrmem * rd_percent_physmem) / 100;
296*7c478bd9Sstevel@tonic-gate }
297*7c478bd9Sstevel@tonic-gate 
298*7c478bd9Sstevel@tonic-gate /*
299*7c478bd9Sstevel@tonic-gate  * Allocate enough physical pages to hold `size' bytes.  Returns an
300*7c478bd9Sstevel@tonic-gate  * array of page_t * pointers that can later be mapped in or out via
301*7c478bd9Sstevel@tonic-gate  * rd_{un}map_window() but is otherwise opaque, or NULL on failure.
302*7c478bd9Sstevel@tonic-gate  *
303*7c478bd9Sstevel@tonic-gate  * This code stolen from the NCA driver.
304*7c478bd9Sstevel@tonic-gate  */
305*7c478bd9Sstevel@tonic-gate page_t **
306*7c478bd9Sstevel@tonic-gate rd_phys_alloc(pgcnt_t npages)
307*7c478bd9Sstevel@tonic-gate {
308*7c478bd9Sstevel@tonic-gate 	page_t		*pp, **ppa;
309*7c478bd9Sstevel@tonic-gate 	pgcnt_t		i;
310*7c478bd9Sstevel@tonic-gate 	size_t		ppalen =  npages * sizeof (struct page_t *);
311*7c478bd9Sstevel@tonic-gate 	struct seg	kseg;
312*7c478bd9Sstevel@tonic-gate 	char		*addr;		/* For the purposes of coloring */
313*7c478bd9Sstevel@tonic-gate 
314*7c478bd9Sstevel@tonic-gate 	if (rd_tot_physmem + npages > rd_max_physmem) {
315*7c478bd9Sstevel@tonic-gate 		return (NULL);
316*7c478bd9Sstevel@tonic-gate 	}
317*7c478bd9Sstevel@tonic-gate 	ppa = kmem_zalloc(ppalen, KM_SLEEP);
318*7c478bd9Sstevel@tonic-gate 	(void) page_resv(npages, KM_SLEEP);
319*7c478bd9Sstevel@tonic-gate 
320*7c478bd9Sstevel@tonic-gate 	for (i = 0, addr = NULL; i < npages; ++i, addr += PAGESIZE) {
321*7c478bd9Sstevel@tonic-gate 		if (!page_create_wait(1, KM_SLEEP)) {
322*7c478bd9Sstevel@tonic-gate 			goto out;
323*7c478bd9Sstevel@tonic-gate 		}
324*7c478bd9Sstevel@tonic-gate 
325*7c478bd9Sstevel@tonic-gate 		kseg.s_as = &kas;
326*7c478bd9Sstevel@tonic-gate 
327*7c478bd9Sstevel@tonic-gate 		if ((pp = page_get_freelist(&kvp, 0, &kseg, addr, PAGESIZE,
328*7c478bd9Sstevel@tonic-gate 		    KM_SLEEP, NULL)) == NULL) {
329*7c478bd9Sstevel@tonic-gate 			if ((pp = page_get_cachelist(&kvp, 0, &kseg, addr,
330*7c478bd9Sstevel@tonic-gate 			    KM_SLEEP, NULL)) == NULL) {
331*7c478bd9Sstevel@tonic-gate 				goto out;
332*7c478bd9Sstevel@tonic-gate 			}
333*7c478bd9Sstevel@tonic-gate 			if (PP_ISAGED(pp) == 0) {
334*7c478bd9Sstevel@tonic-gate 				page_hashout(pp, NULL);
335*7c478bd9Sstevel@tonic-gate 			}
336*7c478bd9Sstevel@tonic-gate 		}
337*7c478bd9Sstevel@tonic-gate 
338*7c478bd9Sstevel@tonic-gate 		PP_CLRFREE(pp);
339*7c478bd9Sstevel@tonic-gate 		PP_CLRAGED(pp);
340*7c478bd9Sstevel@tonic-gate 		ppa[i] = pp;
341*7c478bd9Sstevel@tonic-gate 		page_downgrade(pp);
342*7c478bd9Sstevel@tonic-gate 	}
343*7c478bd9Sstevel@tonic-gate 	rd_tot_physmem += npages;
344*7c478bd9Sstevel@tonic-gate 
345*7c478bd9Sstevel@tonic-gate 	return (ppa);
346*7c478bd9Sstevel@tonic-gate out:
347*7c478bd9Sstevel@tonic-gate 	for (i = 0; ppa[i] != NULL && i < npages; ++i) {
348*7c478bd9Sstevel@tonic-gate 		page_free(ppa[i], 0);
349*7c478bd9Sstevel@tonic-gate 	}
350*7c478bd9Sstevel@tonic-gate 
351*7c478bd9Sstevel@tonic-gate 	page_create_putback(i);
352*7c478bd9Sstevel@tonic-gate 	kmem_free(ppa, ppalen);
353*7c478bd9Sstevel@tonic-gate 
354*7c478bd9Sstevel@tonic-gate 	page_unresv(npages);
355*7c478bd9Sstevel@tonic-gate 
356*7c478bd9Sstevel@tonic-gate 	return (NULL);
357*7c478bd9Sstevel@tonic-gate }
358*7c478bd9Sstevel@tonic-gate 
359*7c478bd9Sstevel@tonic-gate /*
360*7c478bd9Sstevel@tonic-gate  * Free physical pages previously allocated via rd_phys_alloc(); note that
361*7c478bd9Sstevel@tonic-gate  * this function may block as it has to wait until it can exclusively lock
362*7c478bd9Sstevel@tonic-gate  * all the pages first.
363*7c478bd9Sstevel@tonic-gate  */
364*7c478bd9Sstevel@tonic-gate static void
365*7c478bd9Sstevel@tonic-gate rd_phys_free(page_t **ppa, pgcnt_t npages)
366*7c478bd9Sstevel@tonic-gate {
367*7c478bd9Sstevel@tonic-gate 	pgcnt_t	i;
368*7c478bd9Sstevel@tonic-gate 	size_t	ppalen = npages * sizeof (struct page_t *);
369*7c478bd9Sstevel@tonic-gate 
370*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < npages; ++i) {
371*7c478bd9Sstevel@tonic-gate 		if (! page_tryupgrade(ppa[i])) {
372*7c478bd9Sstevel@tonic-gate 			page_unlock(ppa[i]);
373*7c478bd9Sstevel@tonic-gate 			while (! page_lock(ppa[i], SE_EXCL, NULL, P_RECLAIM))
374*7c478bd9Sstevel@tonic-gate 				;
375*7c478bd9Sstevel@tonic-gate 		}
376*7c478bd9Sstevel@tonic-gate 		page_free(ppa[i], 0);
377*7c478bd9Sstevel@tonic-gate 	}
378*7c478bd9Sstevel@tonic-gate 
379*7c478bd9Sstevel@tonic-gate 	kmem_free(ppa, ppalen);
380*7c478bd9Sstevel@tonic-gate 
381*7c478bd9Sstevel@tonic-gate 	page_unresv(npages);
382*7c478bd9Sstevel@tonic-gate 	rd_tot_physmem -= npages;
383*7c478bd9Sstevel@tonic-gate }
384*7c478bd9Sstevel@tonic-gate 
385*7c478bd9Sstevel@tonic-gate /*
386*7c478bd9Sstevel@tonic-gate  * Remove a window mapping (if present).
387*7c478bd9Sstevel@tonic-gate  */
388*7c478bd9Sstevel@tonic-gate static void
389*7c478bd9Sstevel@tonic-gate rd_unmap_window(rd_devstate_t *rsp)
390*7c478bd9Sstevel@tonic-gate {
391*7c478bd9Sstevel@tonic-gate 	if (rsp->rd_window_base != RD_WINDOW_NOT_MAPPED) {
392*7c478bd9Sstevel@tonic-gate 		hat_unload(kas.a_hat, rsp->rd_window_virt, rsp->rd_window_size,
393*7c478bd9Sstevel@tonic-gate 		    HAT_UNLOAD_UNLOCK);
394*7c478bd9Sstevel@tonic-gate 	}
395*7c478bd9Sstevel@tonic-gate }
396*7c478bd9Sstevel@tonic-gate 
397*7c478bd9Sstevel@tonic-gate /*
398*7c478bd9Sstevel@tonic-gate  * Map a portion of the ramdisk into the virtual window.
399*7c478bd9Sstevel@tonic-gate  */
400*7c478bd9Sstevel@tonic-gate static void
401*7c478bd9Sstevel@tonic-gate rd_map_window(rd_devstate_t *rsp, off_t offset)
402*7c478bd9Sstevel@tonic-gate {
403*7c478bd9Sstevel@tonic-gate 	pgcnt_t	offpgs = btop(offset);
404*7c478bd9Sstevel@tonic-gate 
405*7c478bd9Sstevel@tonic-gate 	if (rsp->rd_window_base != RD_WINDOW_NOT_MAPPED) {
406*7c478bd9Sstevel@tonic-gate 		/*
407*7c478bd9Sstevel@tonic-gate 		 * Already mapped; is offset within our window?
408*7c478bd9Sstevel@tonic-gate 		 */
409*7c478bd9Sstevel@tonic-gate 		if (offset >= rsp->rd_window_base &&
410*7c478bd9Sstevel@tonic-gate 		    offset < rsp->rd_window_base + rsp->rd_window_size) {
411*7c478bd9Sstevel@tonic-gate 			return;
412*7c478bd9Sstevel@tonic-gate 		}
413*7c478bd9Sstevel@tonic-gate 
414*7c478bd9Sstevel@tonic-gate 		/*
415*7c478bd9Sstevel@tonic-gate 		 * No, we need to re-map; toss the old mapping.
416*7c478bd9Sstevel@tonic-gate 		 */
417*7c478bd9Sstevel@tonic-gate 		rd_unmap_window(rsp);
418*7c478bd9Sstevel@tonic-gate 	}
419*7c478bd9Sstevel@tonic-gate 	rsp->rd_window_base = ptob(offpgs);
420*7c478bd9Sstevel@tonic-gate 
421*7c478bd9Sstevel@tonic-gate 	/*
422*7c478bd9Sstevel@tonic-gate 	 * Different algorithms depending on whether this is a real
423*7c478bd9Sstevel@tonic-gate 	 * OBP-created ramdisk, or a pseudo ramdisk.
424*7c478bd9Sstevel@tonic-gate 	 */
425*7c478bd9Sstevel@tonic-gate 	if (rsp->rd_dip == rd_dip) {
426*7c478bd9Sstevel@tonic-gate 		pgcnt_t	pi, lastpi;
427*7c478bd9Sstevel@tonic-gate 		caddr_t	vaddr;
428*7c478bd9Sstevel@tonic-gate 
429*7c478bd9Sstevel@tonic-gate 		/*
430*7c478bd9Sstevel@tonic-gate 		 * Find the range of pages which should be mapped.
431*7c478bd9Sstevel@tonic-gate 		 */
432*7c478bd9Sstevel@tonic-gate 		pi = offpgs;
433*7c478bd9Sstevel@tonic-gate 		lastpi = pi + btopr(rsp->rd_window_size);
434*7c478bd9Sstevel@tonic-gate 		if (lastpi > rsp->rd_npages) {
435*7c478bd9Sstevel@tonic-gate 			lastpi = rsp->rd_npages;
436*7c478bd9Sstevel@tonic-gate 		}
437*7c478bd9Sstevel@tonic-gate 
438*7c478bd9Sstevel@tonic-gate 		/*
439*7c478bd9Sstevel@tonic-gate 		 * Load the mapping.
440*7c478bd9Sstevel@tonic-gate 		 */
441*7c478bd9Sstevel@tonic-gate 		vaddr = rsp->rd_window_virt;
442*7c478bd9Sstevel@tonic-gate 		for (; pi < lastpi; ++pi) {
443*7c478bd9Sstevel@tonic-gate 			hat_memload(kas.a_hat, vaddr, rsp->rd_ppa[pi],
444*7c478bd9Sstevel@tonic-gate 			    (PROT_READ | PROT_WRITE) | HAT_NOSYNC,
445*7c478bd9Sstevel@tonic-gate 			    HAT_LOAD_LOCK);
446*7c478bd9Sstevel@tonic-gate 			vaddr += ptob(1);
447*7c478bd9Sstevel@tonic-gate 		}
448*7c478bd9Sstevel@tonic-gate 	} else {
449*7c478bd9Sstevel@tonic-gate 		uint_t	i;
450*7c478bd9Sstevel@tonic-gate 		pfn_t	pfn;
451*7c478bd9Sstevel@tonic-gate 
452*7c478bd9Sstevel@tonic-gate 		/*
453*7c478bd9Sstevel@tonic-gate 		 * Real OBP-created ramdisk: locate the physical range which
454*7c478bd9Sstevel@tonic-gate 		 * contains this offset.
455*7c478bd9Sstevel@tonic-gate 		 */
456*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < rsp->rd_nexisting; ++i) {
457*7c478bd9Sstevel@tonic-gate 			if (offset < rsp->rd_existing[i].size) {
458*7c478bd9Sstevel@tonic-gate 				break;
459*7c478bd9Sstevel@tonic-gate 			}
460*7c478bd9Sstevel@tonic-gate 			offset -= rsp->rd_existing[i].size;
461*7c478bd9Sstevel@tonic-gate 		}
462*7c478bd9Sstevel@tonic-gate 		ASSERT(i < rsp->rd_nexisting);
463*7c478bd9Sstevel@tonic-gate 
464*7c478bd9Sstevel@tonic-gate 		/*
465*7c478bd9Sstevel@tonic-gate 		 * Load the mapping.
466*7c478bd9Sstevel@tonic-gate 		 */
467*7c478bd9Sstevel@tonic-gate 		pfn = btop(rsp->rd_existing[i].phys + offset);
468*7c478bd9Sstevel@tonic-gate 		hat_devload(kas.a_hat, rsp->rd_window_virt, rsp->rd_window_size,
469*7c478bd9Sstevel@tonic-gate 		    pfn, (PROT_READ | PROT_WRITE),
470*7c478bd9Sstevel@tonic-gate 		    HAT_LOAD_NOCONSIST | HAT_LOAD_LOCK);
471*7c478bd9Sstevel@tonic-gate 	}
472*7c478bd9Sstevel@tonic-gate }
473*7c478bd9Sstevel@tonic-gate 
474*7c478bd9Sstevel@tonic-gate /*
475*7c478bd9Sstevel@tonic-gate  * Fakes up a disk geometry, and one big partition, based on the size
476*7c478bd9Sstevel@tonic-gate  * of the file. This is needed because we allow newfs'ing the device,
477*7c478bd9Sstevel@tonic-gate  * and newfs will do several disk ioctls to figure out the geometry and
478*7c478bd9Sstevel@tonic-gate  * partition information. It uses that information to determine the parameters
479*7c478bd9Sstevel@tonic-gate  * to pass to mkfs. Geometry is pretty much irrelevent these days, but we
480*7c478bd9Sstevel@tonic-gate  * have to support it.
481*7c478bd9Sstevel@tonic-gate  *
482*7c478bd9Sstevel@tonic-gate  * Stolen from lofi.c - should maybe split out common code sometime.
483*7c478bd9Sstevel@tonic-gate  */
484*7c478bd9Sstevel@tonic-gate static void
485*7c478bd9Sstevel@tonic-gate rd_fake_disk_geometry(rd_devstate_t *rsp)
486*7c478bd9Sstevel@tonic-gate {
487*7c478bd9Sstevel@tonic-gate 	/* dk_geom - see dkio(7I) */
488*7c478bd9Sstevel@tonic-gate 	/*
489*7c478bd9Sstevel@tonic-gate 	 * dkg_ncyl _could_ be set to one here (one big cylinder with gobs
490*7c478bd9Sstevel@tonic-gate 	 * of sectors), but that breaks programs like fdisk which want to
491*7c478bd9Sstevel@tonic-gate 	 * partition a disk by cylinder. With one cylinder, you can't create
492*7c478bd9Sstevel@tonic-gate 	 * an fdisk partition and put pcfs on it for testing (hard to pick
493*7c478bd9Sstevel@tonic-gate 	 * a number between one and one).
494*7c478bd9Sstevel@tonic-gate 	 *
495*7c478bd9Sstevel@tonic-gate 	 * The cheezy floppy test is an attempt to not have too few cylinders
496*7c478bd9Sstevel@tonic-gate 	 * for a small file, or so many on a big file that you waste space
497*7c478bd9Sstevel@tonic-gate 	 * for backup superblocks or cylinder group structures.
498*7c478bd9Sstevel@tonic-gate 	 */
499*7c478bd9Sstevel@tonic-gate 	if (rsp->rd_size < (2 * 1024 * 1024)) /* floppy? */
500*7c478bd9Sstevel@tonic-gate 		rsp->rd_dkg.dkg_ncyl = rsp->rd_size / (100 * 1024);
501*7c478bd9Sstevel@tonic-gate 	else
502*7c478bd9Sstevel@tonic-gate 		rsp->rd_dkg.dkg_ncyl = rsp->rd_size / (300 * 1024);
503*7c478bd9Sstevel@tonic-gate 	/* in case file file is < 100k */
504*7c478bd9Sstevel@tonic-gate 	if (rsp->rd_dkg.dkg_ncyl == 0)
505*7c478bd9Sstevel@tonic-gate 		rsp->rd_dkg.dkg_ncyl = 1;
506*7c478bd9Sstevel@tonic-gate 	rsp->rd_dkg.dkg_acyl = 0;
507*7c478bd9Sstevel@tonic-gate 	rsp->rd_dkg.dkg_bcyl = 0;
508*7c478bd9Sstevel@tonic-gate 	rsp->rd_dkg.dkg_nhead = 1;
509*7c478bd9Sstevel@tonic-gate 	rsp->rd_dkg.dkg_obs1 = 0;
510*7c478bd9Sstevel@tonic-gate 	rsp->rd_dkg.dkg_intrlv = 0;
511*7c478bd9Sstevel@tonic-gate 	rsp->rd_dkg.dkg_obs2 = 0;
512*7c478bd9Sstevel@tonic-gate 	rsp->rd_dkg.dkg_obs3 = 0;
513*7c478bd9Sstevel@tonic-gate 	rsp->rd_dkg.dkg_apc = 0;
514*7c478bd9Sstevel@tonic-gate 	rsp->rd_dkg.dkg_rpm = 7200;
515*7c478bd9Sstevel@tonic-gate 	rsp->rd_dkg.dkg_pcyl = rsp->rd_dkg.dkg_ncyl + rsp->rd_dkg.dkg_acyl;
516*7c478bd9Sstevel@tonic-gate 	rsp->rd_dkg.dkg_nsect = rsp->rd_size /
517*7c478bd9Sstevel@tonic-gate 	    (DEV_BSIZE * rsp->rd_dkg.dkg_ncyl);
518*7c478bd9Sstevel@tonic-gate 	rsp->rd_dkg.dkg_write_reinstruct = 0;
519*7c478bd9Sstevel@tonic-gate 	rsp->rd_dkg.dkg_read_reinstruct = 0;
520*7c478bd9Sstevel@tonic-gate 
521*7c478bd9Sstevel@tonic-gate 	/* vtoc - see dkio(7I) */
522*7c478bd9Sstevel@tonic-gate 	bzero(&rsp->rd_vtoc, sizeof (struct vtoc));
523*7c478bd9Sstevel@tonic-gate 	rsp->rd_vtoc.v_sanity = VTOC_SANE;
524*7c478bd9Sstevel@tonic-gate 	rsp->rd_vtoc.v_version = V_VERSION;
525*7c478bd9Sstevel@tonic-gate 	bcopy(RD_DRIVER_NAME, rsp->rd_vtoc.v_volume, 7);
526*7c478bd9Sstevel@tonic-gate 	rsp->rd_vtoc.v_sectorsz = DEV_BSIZE;
527*7c478bd9Sstevel@tonic-gate 	rsp->rd_vtoc.v_nparts = 1;
528*7c478bd9Sstevel@tonic-gate 	rsp->rd_vtoc.v_part[0].p_tag = V_UNASSIGNED;
529*7c478bd9Sstevel@tonic-gate 	rsp->rd_vtoc.v_part[0].p_flag = V_UNMNT;
530*7c478bd9Sstevel@tonic-gate 	rsp->rd_vtoc.v_part[0].p_start = (daddr_t)0;
531*7c478bd9Sstevel@tonic-gate 	/*
532*7c478bd9Sstevel@tonic-gate 	 * The partition size cannot just be the number of sectors, because
533*7c478bd9Sstevel@tonic-gate 	 * that might not end on a cylinder boundary. And if that's the case,
534*7c478bd9Sstevel@tonic-gate 	 * newfs/mkfs will print a scary warning. So just figure the size
535*7c478bd9Sstevel@tonic-gate 	 * based on the number of cylinders and sectors/cylinder.
536*7c478bd9Sstevel@tonic-gate 	 */
537*7c478bd9Sstevel@tonic-gate 	rsp->rd_vtoc.v_part[0].p_size = rsp->rd_dkg.dkg_pcyl *
538*7c478bd9Sstevel@tonic-gate 	    rsp->rd_dkg.dkg_nsect * rsp->rd_dkg.dkg_nhead;
539*7c478bd9Sstevel@tonic-gate 
540*7c478bd9Sstevel@tonic-gate 	/* dk_cinfo - see dkio(7I) */
541*7c478bd9Sstevel@tonic-gate 	bzero(&rsp->rd_ci, sizeof (struct dk_cinfo));
542*7c478bd9Sstevel@tonic-gate 	(void) strcpy(rsp->rd_ci.dki_cname, RD_DRIVER_NAME);
543*7c478bd9Sstevel@tonic-gate 	rsp->rd_ci.dki_ctype = DKC_MD;
544*7c478bd9Sstevel@tonic-gate 	rsp->rd_ci.dki_flags = 0;
545*7c478bd9Sstevel@tonic-gate 	rsp->rd_ci.dki_cnum = 0;
546*7c478bd9Sstevel@tonic-gate 	rsp->rd_ci.dki_addr = 0;
547*7c478bd9Sstevel@tonic-gate 	rsp->rd_ci.dki_space = 0;
548*7c478bd9Sstevel@tonic-gate 	rsp->rd_ci.dki_prio = 0;
549*7c478bd9Sstevel@tonic-gate 	rsp->rd_ci.dki_vec = 0;
550*7c478bd9Sstevel@tonic-gate 	(void) strcpy(rsp->rd_ci.dki_dname, RD_DRIVER_NAME);
551*7c478bd9Sstevel@tonic-gate 	rsp->rd_ci.dki_unit = 0;
552*7c478bd9Sstevel@tonic-gate 	rsp->rd_ci.dki_slave = 0;
553*7c478bd9Sstevel@tonic-gate 	rsp->rd_ci.dki_partition = 0;
554*7c478bd9Sstevel@tonic-gate 	/*
555*7c478bd9Sstevel@tonic-gate 	 * newfs uses this to set maxcontig. Must not be < 16, or it
556*7c478bd9Sstevel@tonic-gate 	 * will be 0 when newfs multiplies it by DEV_BSIZE and divides
557*7c478bd9Sstevel@tonic-gate 	 * it by the block size. Then tunefs doesn't work because
558*7c478bd9Sstevel@tonic-gate 	 * maxcontig is 0.
559*7c478bd9Sstevel@tonic-gate 	 */
560*7c478bd9Sstevel@tonic-gate 	rsp->rd_ci.dki_maxtransfer = 16;
561*7c478bd9Sstevel@tonic-gate }
562*7c478bd9Sstevel@tonic-gate 
563*7c478bd9Sstevel@tonic-gate /*
564*7c478bd9Sstevel@tonic-gate  * Deallocate resources (virtual and physical, device nodes, structures)
565*7c478bd9Sstevel@tonic-gate  * from a ramdisk.
566*7c478bd9Sstevel@tonic-gate  */
567*7c478bd9Sstevel@tonic-gate static void
568*7c478bd9Sstevel@tonic-gate rd_dealloc_resources(rd_devstate_t *rsp)
569*7c478bd9Sstevel@tonic-gate {
570*7c478bd9Sstevel@tonic-gate 	dev_info_t	*dip = rsp->rd_dip;
571*7c478bd9Sstevel@tonic-gate 	char		namebuf[RD_NAME_LEN + 5];
572*7c478bd9Sstevel@tonic-gate 	dev_t		fulldev;
573*7c478bd9Sstevel@tonic-gate 
574*7c478bd9Sstevel@tonic-gate 	if (rsp->rd_window_virt != NULL) {
575*7c478bd9Sstevel@tonic-gate 		if (rsp->rd_window_base != RD_WINDOW_NOT_MAPPED) {
576*7c478bd9Sstevel@tonic-gate 			rd_unmap_window(rsp);
577*7c478bd9Sstevel@tonic-gate 		}
578*7c478bd9Sstevel@tonic-gate 		vmem_free(heap_arena, rsp->rd_window_virt, rsp->rd_window_size);
579*7c478bd9Sstevel@tonic-gate 	}
580*7c478bd9Sstevel@tonic-gate 	mutex_destroy(&rsp->rd_device_lock);
581*7c478bd9Sstevel@tonic-gate 
582*7c478bd9Sstevel@tonic-gate 	if (rsp->rd_existing) {
583*7c478bd9Sstevel@tonic-gate 		ddi_prop_free(rsp->rd_existing);
584*7c478bd9Sstevel@tonic-gate 	}
585*7c478bd9Sstevel@tonic-gate 	if (rsp->rd_ppa != NULL) {
586*7c478bd9Sstevel@tonic-gate 		rd_phys_free(rsp->rd_ppa, rsp->rd_npages);
587*7c478bd9Sstevel@tonic-gate 	}
588*7c478bd9Sstevel@tonic-gate 
589*7c478bd9Sstevel@tonic-gate 	/*
590*7c478bd9Sstevel@tonic-gate 	 * Remove the block and raw device nodes.
591*7c478bd9Sstevel@tonic-gate 	 */
592*7c478bd9Sstevel@tonic-gate 	if (dip == rd_dip) {
593*7c478bd9Sstevel@tonic-gate 		(void) snprintf(namebuf, sizeof (namebuf), "%s",
594*7c478bd9Sstevel@tonic-gate 		    rsp->rd_name);
595*7c478bd9Sstevel@tonic-gate 		ddi_remove_minor_node(dip, namebuf);
596*7c478bd9Sstevel@tonic-gate 		(void) snprintf(namebuf, sizeof (namebuf), "%s,raw",
597*7c478bd9Sstevel@tonic-gate 		    rsp->rd_name);
598*7c478bd9Sstevel@tonic-gate 		ddi_remove_minor_node(dip, namebuf);
599*7c478bd9Sstevel@tonic-gate 	} else {
600*7c478bd9Sstevel@tonic-gate 		ddi_remove_minor_node(dip, "a");
601*7c478bd9Sstevel@tonic-gate 		ddi_remove_minor_node(dip, "a,raw");
602*7c478bd9Sstevel@tonic-gate 	}
603*7c478bd9Sstevel@tonic-gate 
604*7c478bd9Sstevel@tonic-gate 	/*
605*7c478bd9Sstevel@tonic-gate 	 * Remove the "Size" and "Nblocks" properties.
606*7c478bd9Sstevel@tonic-gate 	 */
607*7c478bd9Sstevel@tonic-gate 	fulldev = makedevice(ddi_driver_major(dip), rsp->rd_minor);
608*7c478bd9Sstevel@tonic-gate 	(void) ddi_prop_remove(fulldev, dip, SIZE_PROP_NAME);
609*7c478bd9Sstevel@tonic-gate 	(void) ddi_prop_remove(fulldev, dip, NBLOCKS_PROP_NAME);
610*7c478bd9Sstevel@tonic-gate 
611*7c478bd9Sstevel@tonic-gate 	if (rsp->rd_kstat) {
612*7c478bd9Sstevel@tonic-gate 		kstat_delete(rsp->rd_kstat);
613*7c478bd9Sstevel@tonic-gate 		mutex_destroy(&rsp->rd_kstat_lock);
614*7c478bd9Sstevel@tonic-gate 	}
615*7c478bd9Sstevel@tonic-gate 
616*7c478bd9Sstevel@tonic-gate 	ddi_soft_state_free(rd_statep, rsp->rd_minor);
617*7c478bd9Sstevel@tonic-gate }
618*7c478bd9Sstevel@tonic-gate 
619*7c478bd9Sstevel@tonic-gate /*
620*7c478bd9Sstevel@tonic-gate  * Allocate resources (virtual and physical, device nodes, structures)
621*7c478bd9Sstevel@tonic-gate  * to a ramdisk.
622*7c478bd9Sstevel@tonic-gate  */
623*7c478bd9Sstevel@tonic-gate static rd_devstate_t *
624*7c478bd9Sstevel@tonic-gate rd_alloc_resources(char *name, size_t size, dev_info_t *dip)
625*7c478bd9Sstevel@tonic-gate {
626*7c478bd9Sstevel@tonic-gate 	minor_t		minor;
627*7c478bd9Sstevel@tonic-gate 	rd_devstate_t	*rsp;
628*7c478bd9Sstevel@tonic-gate 	char		namebuf[RD_NAME_LEN + 5];
629*7c478bd9Sstevel@tonic-gate 	dev_t		fulldev;
630*7c478bd9Sstevel@tonic-gate 	int64_t		Nblocks_prop_val;
631*7c478bd9Sstevel@tonic-gate 	int64_t		Size_prop_val;
632*7c478bd9Sstevel@tonic-gate 
633*7c478bd9Sstevel@tonic-gate 	minor = rd_find_free_minor();
634*7c478bd9Sstevel@tonic-gate 	if (ddi_soft_state_zalloc(rd_statep, minor) == DDI_FAILURE) {
635*7c478bd9Sstevel@tonic-gate 		return (NULL);
636*7c478bd9Sstevel@tonic-gate 	}
637*7c478bd9Sstevel@tonic-gate 	rsp = ddi_get_soft_state(rd_statep, minor);
638*7c478bd9Sstevel@tonic-gate 
639*7c478bd9Sstevel@tonic-gate 	(void) strcpy(rsp->rd_name, name);
640*7c478bd9Sstevel@tonic-gate 	rsp->rd_dip = dip;
641*7c478bd9Sstevel@tonic-gate 	rsp->rd_minor = minor;
642*7c478bd9Sstevel@tonic-gate 	rsp->rd_size = size;
643*7c478bd9Sstevel@tonic-gate 
644*7c478bd9Sstevel@tonic-gate 	/*
645*7c478bd9Sstevel@tonic-gate 	 * Allocate virtual window onto ramdisk.
646*7c478bd9Sstevel@tonic-gate 	 */
647*7c478bd9Sstevel@tonic-gate 	mutex_init(&rsp->rd_device_lock, NULL, MUTEX_DRIVER, NULL);
648*7c478bd9Sstevel@tonic-gate 	rsp->rd_window_base = RD_WINDOW_NOT_MAPPED;
649*7c478bd9Sstevel@tonic-gate 	rsp->rd_window_size = PAGESIZE;
650*7c478bd9Sstevel@tonic-gate 	rsp->rd_window_virt = vmem_alloc(heap_arena,
651*7c478bd9Sstevel@tonic-gate 	    rsp->rd_window_size, VM_SLEEP);
652*7c478bd9Sstevel@tonic-gate 	if (rsp->rd_window_virt == NULL) {
653*7c478bd9Sstevel@tonic-gate 		goto create_failed;
654*7c478bd9Sstevel@tonic-gate 	}
655*7c478bd9Sstevel@tonic-gate 
656*7c478bd9Sstevel@tonic-gate 	/*
657*7c478bd9Sstevel@tonic-gate 	 * Allocate physical memory for non-OBP ramdisks.
658*7c478bd9Sstevel@tonic-gate 	 * Create pseudo block and raw device nodes.
659*7c478bd9Sstevel@tonic-gate 	 */
660*7c478bd9Sstevel@tonic-gate 	if (dip == rd_dip) {
661*7c478bd9Sstevel@tonic-gate 		rsp->rd_npages = btopr(size);
662*7c478bd9Sstevel@tonic-gate 		rsp->rd_ppa = rd_phys_alloc(rsp->rd_npages);
663*7c478bd9Sstevel@tonic-gate 		if (rsp->rd_ppa == NULL) {
664*7c478bd9Sstevel@tonic-gate 			goto create_failed;
665*7c478bd9Sstevel@tonic-gate 		}
666*7c478bd9Sstevel@tonic-gate 
667*7c478bd9Sstevel@tonic-gate 		/*
668*7c478bd9Sstevel@tonic-gate 		 * For non-OBP ramdisks the device nodes are:
669*7c478bd9Sstevel@tonic-gate 		 *
670*7c478bd9Sstevel@tonic-gate 		 *	/devices/pseudo/ramdisk@0:<diskname>
671*7c478bd9Sstevel@tonic-gate 		 *	/devices/pseudo/ramdisk@0:<diskname>,raw
672*7c478bd9Sstevel@tonic-gate 		 */
673*7c478bd9Sstevel@tonic-gate 		(void) snprintf(namebuf, sizeof (namebuf), "%s",
674*7c478bd9Sstevel@tonic-gate 		    rsp->rd_name);
675*7c478bd9Sstevel@tonic-gate 		if (ddi_create_minor_node(dip, namebuf, S_IFBLK, minor,
676*7c478bd9Sstevel@tonic-gate 		    DDI_PSEUDO, 0) == DDI_FAILURE) {
677*7c478bd9Sstevel@tonic-gate 			goto create_failed;
678*7c478bd9Sstevel@tonic-gate 		}
679*7c478bd9Sstevel@tonic-gate 		(void) snprintf(namebuf, sizeof (namebuf), "%s,raw",
680*7c478bd9Sstevel@tonic-gate 		    rsp->rd_name);
681*7c478bd9Sstevel@tonic-gate 		if (ddi_create_minor_node(dip, namebuf, S_IFCHR, minor,
682*7c478bd9Sstevel@tonic-gate 		    DDI_PSEUDO, 0) == DDI_FAILURE) {
683*7c478bd9Sstevel@tonic-gate 			goto create_failed;
684*7c478bd9Sstevel@tonic-gate 		}
685*7c478bd9Sstevel@tonic-gate 	} else {
686*7c478bd9Sstevel@tonic-gate 		/*
687*7c478bd9Sstevel@tonic-gate 		 * For OBP-created ramdisks the device nodes are:
688*7c478bd9Sstevel@tonic-gate 		 *
689*7c478bd9Sstevel@tonic-gate 		 *	/devices/ramdisk-<diskname>:a
690*7c478bd9Sstevel@tonic-gate 		 *	/devices/ramdisk-<diskname>:a,raw
691*7c478bd9Sstevel@tonic-gate 		 */
692*7c478bd9Sstevel@tonic-gate 		if (ddi_create_minor_node(dip, "a", S_IFBLK, minor,
693*7c478bd9Sstevel@tonic-gate 		    DDI_PSEUDO, 0) == DDI_FAILURE) {
694*7c478bd9Sstevel@tonic-gate 			goto create_failed;
695*7c478bd9Sstevel@tonic-gate 		}
696*7c478bd9Sstevel@tonic-gate 		if (ddi_create_minor_node(dip, "a,raw", S_IFCHR, minor,
697*7c478bd9Sstevel@tonic-gate 		    DDI_PSEUDO, 0) == DDI_FAILURE) {
698*7c478bd9Sstevel@tonic-gate 			goto create_failed;
699*7c478bd9Sstevel@tonic-gate 		}
700*7c478bd9Sstevel@tonic-gate 	}
701*7c478bd9Sstevel@tonic-gate 
702*7c478bd9Sstevel@tonic-gate 	/*
703*7c478bd9Sstevel@tonic-gate 	 * Create the "Size" and "Nblocks" properties.
704*7c478bd9Sstevel@tonic-gate 	 */
705*7c478bd9Sstevel@tonic-gate 	fulldev = makedevice(ddi_driver_major(dip), minor);
706*7c478bd9Sstevel@tonic-gate 	Size_prop_val = size;
707*7c478bd9Sstevel@tonic-gate 	if ((ddi_prop_update_int64(fulldev, dip,
708*7c478bd9Sstevel@tonic-gate 	    SIZE_PROP_NAME, Size_prop_val)) != DDI_PROP_SUCCESS) {
709*7c478bd9Sstevel@tonic-gate 		goto create_failed;
710*7c478bd9Sstevel@tonic-gate 	}
711*7c478bd9Sstevel@tonic-gate 	Nblocks_prop_val = size / DEV_BSIZE;
712*7c478bd9Sstevel@tonic-gate 	if ((ddi_prop_update_int64(fulldev, dip,
713*7c478bd9Sstevel@tonic-gate 	    NBLOCKS_PROP_NAME, Nblocks_prop_val)) != DDI_PROP_SUCCESS) {
714*7c478bd9Sstevel@tonic-gate 		goto create_failed;
715*7c478bd9Sstevel@tonic-gate 	}
716*7c478bd9Sstevel@tonic-gate 
717*7c478bd9Sstevel@tonic-gate 	/*
718*7c478bd9Sstevel@tonic-gate 	 * Allocate kstat stuff.
719*7c478bd9Sstevel@tonic-gate 	 */
720*7c478bd9Sstevel@tonic-gate 	rsp->rd_kstat = kstat_create(RD_DRIVER_NAME, minor, NULL,
721*7c478bd9Sstevel@tonic-gate 					"disk", KSTAT_TYPE_IO, 1, 0);
722*7c478bd9Sstevel@tonic-gate 	if (rsp->rd_kstat) {
723*7c478bd9Sstevel@tonic-gate 		mutex_init(&rsp->rd_kstat_lock, NULL,
724*7c478bd9Sstevel@tonic-gate 		    MUTEX_DRIVER, NULL);
725*7c478bd9Sstevel@tonic-gate 		rsp->rd_kstat->ks_lock = &rsp->rd_kstat_lock;
726*7c478bd9Sstevel@tonic-gate 		kstat_install(rsp->rd_kstat);
727*7c478bd9Sstevel@tonic-gate 	}
728*7c478bd9Sstevel@tonic-gate 
729*7c478bd9Sstevel@tonic-gate 	rd_fake_disk_geometry(rsp);
730*7c478bd9Sstevel@tonic-gate 
731*7c478bd9Sstevel@tonic-gate 	return (rsp);
732*7c478bd9Sstevel@tonic-gate 
733*7c478bd9Sstevel@tonic-gate create_failed:
734*7c478bd9Sstevel@tonic-gate 	/*
735*7c478bd9Sstevel@tonic-gate 	 * Cleanup.
736*7c478bd9Sstevel@tonic-gate 	 */
737*7c478bd9Sstevel@tonic-gate 	rd_dealloc_resources(rsp);
738*7c478bd9Sstevel@tonic-gate 
739*7c478bd9Sstevel@tonic-gate 	return (NULL);
740*7c478bd9Sstevel@tonic-gate }
741*7c478bd9Sstevel@tonic-gate 
742*7c478bd9Sstevel@tonic-gate /*
743*7c478bd9Sstevel@tonic-gate  * Undo what we did in rd_attach, freeing resources and removing things which
744*7c478bd9Sstevel@tonic-gate  * we installed.  The system framework guarantees we are not active with this
745*7c478bd9Sstevel@tonic-gate  * devinfo node in any other entry points at this time.
746*7c478bd9Sstevel@tonic-gate  */
747*7c478bd9Sstevel@tonic-gate static int
748*7c478bd9Sstevel@tonic-gate rd_common_detach(dev_info_t *dip)
749*7c478bd9Sstevel@tonic-gate {
750*7c478bd9Sstevel@tonic-gate 	if (dip == rd_dip) {
751*7c478bd9Sstevel@tonic-gate 		/*
752*7c478bd9Sstevel@tonic-gate 		 * Pseudo node: can't detach if any pseudo ramdisks exist.
753*7c478bd9Sstevel@tonic-gate 		 */
754*7c478bd9Sstevel@tonic-gate 		if (rd_is_busy()) {
755*7c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
756*7c478bd9Sstevel@tonic-gate 		}
757*7c478bd9Sstevel@tonic-gate 		ddi_soft_state_free(rd_statep, RD_CTL_MINOR);
758*7c478bd9Sstevel@tonic-gate 		rd_dip = NULL;
759*7c478bd9Sstevel@tonic-gate 	} else {
760*7c478bd9Sstevel@tonic-gate 		/*
761*7c478bd9Sstevel@tonic-gate 		 * A 'real' ramdisk; find the state and free resources.
762*7c478bd9Sstevel@tonic-gate 		 */
763*7c478bd9Sstevel@tonic-gate 		rd_devstate_t	*rsp;
764*7c478bd9Sstevel@tonic-gate 
765*7c478bd9Sstevel@tonic-gate 		if ((rsp = rd_find_dip_state(dip)) != NULL) {
766*7c478bd9Sstevel@tonic-gate 			rd_dealloc_resources(rsp);
767*7c478bd9Sstevel@tonic-gate 		}
768*7c478bd9Sstevel@tonic-gate 	}
769*7c478bd9Sstevel@tonic-gate 	ddi_remove_minor_node(dip, NULL);
770*7c478bd9Sstevel@tonic-gate 
771*7c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
772*7c478bd9Sstevel@tonic-gate }
773*7c478bd9Sstevel@tonic-gate 
774*7c478bd9Sstevel@tonic-gate static int
775*7c478bd9Sstevel@tonic-gate rd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
776*7c478bd9Sstevel@tonic-gate {
777*7c478bd9Sstevel@tonic-gate 	char		*name;
778*7c478bd9Sstevel@tonic-gate 	rd_existing_t	*ep = NULL;
779*7c478bd9Sstevel@tonic-gate 	uint_t		nep, i;
780*7c478bd9Sstevel@tonic-gate 	size_t		size = 0;
781*7c478bd9Sstevel@tonic-gate 	rd_devstate_t	*rsp;
782*7c478bd9Sstevel@tonic-gate 
783*7c478bd9Sstevel@tonic-gate 	switch (cmd) {
784*7c478bd9Sstevel@tonic-gate 
785*7c478bd9Sstevel@tonic-gate 	case DDI_ATTACH:
786*7c478bd9Sstevel@tonic-gate 		mutex_enter(&rd_lock);
787*7c478bd9Sstevel@tonic-gate 
788*7c478bd9Sstevel@tonic-gate 		/*
789*7c478bd9Sstevel@tonic-gate 		 * For pseudo ramdisk devinfo set up state 0 and :ctl device;
790*7c478bd9Sstevel@tonic-gate 		 * else it's an OBP-created ramdisk.
791*7c478bd9Sstevel@tonic-gate 		 */
792*7c478bd9Sstevel@tonic-gate 		if (is_pseudo_device(dip)) {
793*7c478bd9Sstevel@tonic-gate 			rd_dip = dip;
794*7c478bd9Sstevel@tonic-gate 			rd_init_tuneables();
795*7c478bd9Sstevel@tonic-gate 
796*7c478bd9Sstevel@tonic-gate 			/*
797*7c478bd9Sstevel@tonic-gate 			 * The zeroth minor is reserved for the ramdisk
798*7c478bd9Sstevel@tonic-gate 			 * 'control' device.
799*7c478bd9Sstevel@tonic-gate 			 */
800*7c478bd9Sstevel@tonic-gate 			if (ddi_soft_state_zalloc(rd_statep, RD_CTL_MINOR) ==
801*7c478bd9Sstevel@tonic-gate 			    DDI_FAILURE) {
802*7c478bd9Sstevel@tonic-gate 				goto attach_failed;
803*7c478bd9Sstevel@tonic-gate 			}
804*7c478bd9Sstevel@tonic-gate 			rsp = ddi_get_soft_state(rd_statep, RD_CTL_MINOR);
805*7c478bd9Sstevel@tonic-gate 			rsp->rd_dip = dip;
806*7c478bd9Sstevel@tonic-gate 
807*7c478bd9Sstevel@tonic-gate 			if (ddi_create_minor_node(dip, RD_CTL_NODE,
808*7c478bd9Sstevel@tonic-gate 			    S_IFCHR, 0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
809*7c478bd9Sstevel@tonic-gate 				goto attach_failed;
810*7c478bd9Sstevel@tonic-gate 			}
811*7c478bd9Sstevel@tonic-gate 		} else {
812*7c478bd9Sstevel@tonic-gate 			RD_STRIP_PREFIX(name, ddi_node_name(dip));
813*7c478bd9Sstevel@tonic-gate 
814*7c478bd9Sstevel@tonic-gate 			if (strlen(name) > RD_NAME_LEN) {
815*7c478bd9Sstevel@tonic-gate 				cmn_err(CE_CONT,
816*7c478bd9Sstevel@tonic-gate 				    "%s: name too long - ignoring\n", name);
817*7c478bd9Sstevel@tonic-gate 				goto attach_failed;
818*7c478bd9Sstevel@tonic-gate 			}
819*7c478bd9Sstevel@tonic-gate 
820*7c478bd9Sstevel@tonic-gate 			/*
821*7c478bd9Sstevel@tonic-gate 			 * An OBP-created ramdisk must have an 'existing'
822*7c478bd9Sstevel@tonic-gate 			 * property; get and check it.
823*7c478bd9Sstevel@tonic-gate 			 */
824*7c478bd9Sstevel@tonic-gate 			if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip,
825*7c478bd9Sstevel@tonic-gate 			    DDI_PROP_DONTPASS, RD_EXISTING_PROP_NAME,
826*7c478bd9Sstevel@tonic-gate 			    (uchar_t **)&ep, &nep) != DDI_SUCCESS) {
827*7c478bd9Sstevel@tonic-gate 				cmn_err(CE_CONT,
828*7c478bd9Sstevel@tonic-gate 				    "%s: " RD_EXISTING_PROP_NAME
829*7c478bd9Sstevel@tonic-gate 				    " property missing\n", name);
830*7c478bd9Sstevel@tonic-gate 				goto attach_failed;
831*7c478bd9Sstevel@tonic-gate 			}
832*7c478bd9Sstevel@tonic-gate 			if (nep == 0 || (nep % sizeof (*ep)) != 0) {
833*7c478bd9Sstevel@tonic-gate 				cmn_err(CE_CONT,
834*7c478bd9Sstevel@tonic-gate 				    "%s: " RD_EXISTING_PROP_NAME
835*7c478bd9Sstevel@tonic-gate 				    " illegal size\n", name);
836*7c478bd9Sstevel@tonic-gate 				goto attach_failed;
837*7c478bd9Sstevel@tonic-gate 			}
838*7c478bd9Sstevel@tonic-gate 			nep /= sizeof (*ep);
839*7c478bd9Sstevel@tonic-gate 
840*7c478bd9Sstevel@tonic-gate 			/*
841*7c478bd9Sstevel@tonic-gate 			 * Calculate the size of the ramdisk.
842*7c478bd9Sstevel@tonic-gate 			 */
843*7c478bd9Sstevel@tonic-gate 			for (i = 0; i < nep; ++i) {
844*7c478bd9Sstevel@tonic-gate 				size += ep[i].size;
845*7c478bd9Sstevel@tonic-gate 			}
846*7c478bd9Sstevel@tonic-gate 
847*7c478bd9Sstevel@tonic-gate 			/*
848*7c478bd9Sstevel@tonic-gate 			 * Allocate driver resources for the ramdisk.
849*7c478bd9Sstevel@tonic-gate 			 */
850*7c478bd9Sstevel@tonic-gate 			if ((rsp = rd_alloc_resources(name, size,
851*7c478bd9Sstevel@tonic-gate 			    dip)) == NULL) {
852*7c478bd9Sstevel@tonic-gate 				goto attach_failed;
853*7c478bd9Sstevel@tonic-gate 			}
854*7c478bd9Sstevel@tonic-gate 
855*7c478bd9Sstevel@tonic-gate 			rsp->rd_existing = ep;
856*7c478bd9Sstevel@tonic-gate 			rsp->rd_nexisting = nep;
857*7c478bd9Sstevel@tonic-gate 		}
858*7c478bd9Sstevel@tonic-gate 
859*7c478bd9Sstevel@tonic-gate 		mutex_exit(&rd_lock);
860*7c478bd9Sstevel@tonic-gate 
861*7c478bd9Sstevel@tonic-gate 		ddi_report_dev(dip);
862*7c478bd9Sstevel@tonic-gate 
863*7c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
864*7c478bd9Sstevel@tonic-gate 
865*7c478bd9Sstevel@tonic-gate 	case DDI_RESUME:
866*7c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
867*7c478bd9Sstevel@tonic-gate 
868*7c478bd9Sstevel@tonic-gate 	default:
869*7c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
870*7c478bd9Sstevel@tonic-gate 	}
871*7c478bd9Sstevel@tonic-gate 
872*7c478bd9Sstevel@tonic-gate attach_failed:
873*7c478bd9Sstevel@tonic-gate 	/*
874*7c478bd9Sstevel@tonic-gate 	 * Use our common detach routine to unallocate any stuff which
875*7c478bd9Sstevel@tonic-gate 	 * was allocated above.
876*7c478bd9Sstevel@tonic-gate 	 */
877*7c478bd9Sstevel@tonic-gate 	(void) rd_common_detach(dip);
878*7c478bd9Sstevel@tonic-gate 	mutex_exit(&rd_lock);
879*7c478bd9Sstevel@tonic-gate 
880*7c478bd9Sstevel@tonic-gate 	if (ep != NULL) {
881*7c478bd9Sstevel@tonic-gate 		ddi_prop_free(ep);
882*7c478bd9Sstevel@tonic-gate 	}
883*7c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
884*7c478bd9Sstevel@tonic-gate }
885*7c478bd9Sstevel@tonic-gate 
886*7c478bd9Sstevel@tonic-gate static int
887*7c478bd9Sstevel@tonic-gate rd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
888*7c478bd9Sstevel@tonic-gate {
889*7c478bd9Sstevel@tonic-gate 	int	e;
890*7c478bd9Sstevel@tonic-gate 
891*7c478bd9Sstevel@tonic-gate 	switch (cmd) {
892*7c478bd9Sstevel@tonic-gate 
893*7c478bd9Sstevel@tonic-gate 	case DDI_DETACH:
894*7c478bd9Sstevel@tonic-gate 		mutex_enter(&rd_lock);
895*7c478bd9Sstevel@tonic-gate 		e = rd_common_detach(dip);
896*7c478bd9Sstevel@tonic-gate 		mutex_exit(&rd_lock);
897*7c478bd9Sstevel@tonic-gate 
898*7c478bd9Sstevel@tonic-gate 		return (e);
899*7c478bd9Sstevel@tonic-gate 
900*7c478bd9Sstevel@tonic-gate 	case DDI_SUSPEND:
901*7c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
902*7c478bd9Sstevel@tonic-gate 
903*7c478bd9Sstevel@tonic-gate 	default:
904*7c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
905*7c478bd9Sstevel@tonic-gate 	}
906*7c478bd9Sstevel@tonic-gate }
907*7c478bd9Sstevel@tonic-gate 
908*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
909*7c478bd9Sstevel@tonic-gate static int
910*7c478bd9Sstevel@tonic-gate rd_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
911*7c478bd9Sstevel@tonic-gate {
912*7c478bd9Sstevel@tonic-gate 	rd_devstate_t	*rsp;
913*7c478bd9Sstevel@tonic-gate 
914*7c478bd9Sstevel@tonic-gate 	switch (infocmd) {
915*7c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
916*7c478bd9Sstevel@tonic-gate 		if ((rsp = ddi_get_soft_state(rd_statep,
917*7c478bd9Sstevel@tonic-gate 		    getminor((dev_t)arg))) != NULL) {
918*7c478bd9Sstevel@tonic-gate 			*result = rsp->rd_dip;
919*7c478bd9Sstevel@tonic-gate 			return (DDI_SUCCESS);
920*7c478bd9Sstevel@tonic-gate 		}
921*7c478bd9Sstevel@tonic-gate 		*result = NULL;
922*7c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
923*7c478bd9Sstevel@tonic-gate 
924*7c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
925*7c478bd9Sstevel@tonic-gate 		if ((rsp = ddi_get_soft_state(rd_statep,
926*7c478bd9Sstevel@tonic-gate 		    getminor((dev_t)arg))) != NULL) {
927*7c478bd9Sstevel@tonic-gate 			*result = (void *)(uintptr_t)
928*7c478bd9Sstevel@tonic-gate 			    ddi_get_instance(rsp->rd_dip);
929*7c478bd9Sstevel@tonic-gate 			return (DDI_SUCCESS);
930*7c478bd9Sstevel@tonic-gate 		}
931*7c478bd9Sstevel@tonic-gate 		*result = NULL;
932*7c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
933*7c478bd9Sstevel@tonic-gate 
934*7c478bd9Sstevel@tonic-gate 	default:
935*7c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
936*7c478bd9Sstevel@tonic-gate 	}
937*7c478bd9Sstevel@tonic-gate }
938*7c478bd9Sstevel@tonic-gate 
939*7c478bd9Sstevel@tonic-gate /*ARGSUSED3*/
940*7c478bd9Sstevel@tonic-gate static int
941*7c478bd9Sstevel@tonic-gate rd_open(dev_t *devp, int flag, int otyp, cred_t *credp)
942*7c478bd9Sstevel@tonic-gate {
943*7c478bd9Sstevel@tonic-gate 	minor_t		minor;
944*7c478bd9Sstevel@tonic-gate 	rd_devstate_t	*rsp;
945*7c478bd9Sstevel@tonic-gate 
946*7c478bd9Sstevel@tonic-gate 	mutex_enter(&rd_lock);
947*7c478bd9Sstevel@tonic-gate 
948*7c478bd9Sstevel@tonic-gate 	minor = getminor(*devp);
949*7c478bd9Sstevel@tonic-gate 	if (minor == RD_CTL_MINOR) {
950*7c478bd9Sstevel@tonic-gate 		/*
951*7c478bd9Sstevel@tonic-gate 		 * Master control device; must be opened exclusively.
952*7c478bd9Sstevel@tonic-gate 		 */
953*7c478bd9Sstevel@tonic-gate 		if ((flag & FEXCL) != FEXCL || otyp != OTYP_CHR) {
954*7c478bd9Sstevel@tonic-gate 			mutex_exit(&rd_lock);
955*7c478bd9Sstevel@tonic-gate 			return (EINVAL);
956*7c478bd9Sstevel@tonic-gate 		}
957*7c478bd9Sstevel@tonic-gate 
958*7c478bd9Sstevel@tonic-gate 		rsp = ddi_get_soft_state(rd_statep, RD_CTL_MINOR);
959*7c478bd9Sstevel@tonic-gate 		if (rsp == NULL) {
960*7c478bd9Sstevel@tonic-gate 			mutex_exit(&rd_lock);
961*7c478bd9Sstevel@tonic-gate 			return (ENXIO);
962*7c478bd9Sstevel@tonic-gate 		}
963*7c478bd9Sstevel@tonic-gate 
964*7c478bd9Sstevel@tonic-gate 		if (rd_is_open(rsp)) {
965*7c478bd9Sstevel@tonic-gate 			mutex_exit(&rd_lock);
966*7c478bd9Sstevel@tonic-gate 			return (EBUSY);
967*7c478bd9Sstevel@tonic-gate 		}
968*7c478bd9Sstevel@tonic-gate 		(void) rd_opened(rsp, OTYP_CHR);
969*7c478bd9Sstevel@tonic-gate 
970*7c478bd9Sstevel@tonic-gate 		mutex_exit(&rd_lock);
971*7c478bd9Sstevel@tonic-gate 
972*7c478bd9Sstevel@tonic-gate 		return (0);
973*7c478bd9Sstevel@tonic-gate 	}
974*7c478bd9Sstevel@tonic-gate 
975*7c478bd9Sstevel@tonic-gate 	rsp = ddi_get_soft_state(rd_statep, minor);
976*7c478bd9Sstevel@tonic-gate 	if (rsp == NULL) {
977*7c478bd9Sstevel@tonic-gate 		mutex_exit(&rd_lock);
978*7c478bd9Sstevel@tonic-gate 		return (ENXIO);
979*7c478bd9Sstevel@tonic-gate 	}
980*7c478bd9Sstevel@tonic-gate 
981*7c478bd9Sstevel@tonic-gate 	if (rd_opened(rsp, otyp) == -1) {
982*7c478bd9Sstevel@tonic-gate 		mutex_exit(&rd_lock);
983*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
984*7c478bd9Sstevel@tonic-gate 	}
985*7c478bd9Sstevel@tonic-gate 
986*7c478bd9Sstevel@tonic-gate 	mutex_exit(&rd_lock);
987*7c478bd9Sstevel@tonic-gate 	return (0);
988*7c478bd9Sstevel@tonic-gate }
989*7c478bd9Sstevel@tonic-gate 
990*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
991*7c478bd9Sstevel@tonic-gate static int
992*7c478bd9Sstevel@tonic-gate rd_close(dev_t dev, int flag, int otyp, struct cred *credp)
993*7c478bd9Sstevel@tonic-gate {
994*7c478bd9Sstevel@tonic-gate 	minor_t		minor;
995*7c478bd9Sstevel@tonic-gate 	rd_devstate_t	*rsp;
996*7c478bd9Sstevel@tonic-gate 
997*7c478bd9Sstevel@tonic-gate 	mutex_enter(&rd_lock);
998*7c478bd9Sstevel@tonic-gate 
999*7c478bd9Sstevel@tonic-gate 	minor = getminor(dev);
1000*7c478bd9Sstevel@tonic-gate 
1001*7c478bd9Sstevel@tonic-gate 	rsp = ddi_get_soft_state(rd_statep, minor);
1002*7c478bd9Sstevel@tonic-gate 	if (rsp == NULL) {
1003*7c478bd9Sstevel@tonic-gate 		mutex_exit(&rd_lock);
1004*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
1005*7c478bd9Sstevel@tonic-gate 	}
1006*7c478bd9Sstevel@tonic-gate 
1007*7c478bd9Sstevel@tonic-gate 	rd_closed(rsp, otyp);
1008*7c478bd9Sstevel@tonic-gate 
1009*7c478bd9Sstevel@tonic-gate 	mutex_exit(&rd_lock);
1010*7c478bd9Sstevel@tonic-gate 
1011*7c478bd9Sstevel@tonic-gate 	return (0);
1012*7c478bd9Sstevel@tonic-gate }
1013*7c478bd9Sstevel@tonic-gate 
1014*7c478bd9Sstevel@tonic-gate static void
1015*7c478bd9Sstevel@tonic-gate rd_minphys(struct buf *bp)
1016*7c478bd9Sstevel@tonic-gate {
1017*7c478bd9Sstevel@tonic-gate 	if (bp->b_bcount > rd_maxphys) {
1018*7c478bd9Sstevel@tonic-gate 		bp->b_bcount = rd_maxphys;
1019*7c478bd9Sstevel@tonic-gate 	}
1020*7c478bd9Sstevel@tonic-gate }
1021*7c478bd9Sstevel@tonic-gate 
1022*7c478bd9Sstevel@tonic-gate static void
1023*7c478bd9Sstevel@tonic-gate rd_rw(rd_devstate_t *rsp, struct buf *bp, offset_t offset, size_t nbytes)
1024*7c478bd9Sstevel@tonic-gate {
1025*7c478bd9Sstevel@tonic-gate 	int	reading = bp->b_flags & B_READ;
1026*7c478bd9Sstevel@tonic-gate 	caddr_t	buf_addr;
1027*7c478bd9Sstevel@tonic-gate 
1028*7c478bd9Sstevel@tonic-gate 	bp_mapin(bp);
1029*7c478bd9Sstevel@tonic-gate 	buf_addr = bp->b_un.b_addr;
1030*7c478bd9Sstevel@tonic-gate 
1031*7c478bd9Sstevel@tonic-gate 	while (nbytes > 0) {
1032*7c478bd9Sstevel@tonic-gate 		offset_t	off_in_window;
1033*7c478bd9Sstevel@tonic-gate 		size_t		rem_in_window, copy_bytes;
1034*7c478bd9Sstevel@tonic-gate 		caddr_t		raddr;
1035*7c478bd9Sstevel@tonic-gate 
1036*7c478bd9Sstevel@tonic-gate 		mutex_enter(&rsp->rd_device_lock);
1037*7c478bd9Sstevel@tonic-gate 		rd_map_window(rsp, offset);
1038*7c478bd9Sstevel@tonic-gate 
1039*7c478bd9Sstevel@tonic-gate 		off_in_window = offset - rsp->rd_window_base;
1040*7c478bd9Sstevel@tonic-gate 		rem_in_window = rsp->rd_window_size - off_in_window;
1041*7c478bd9Sstevel@tonic-gate 
1042*7c478bd9Sstevel@tonic-gate 		raddr = rsp->rd_window_virt + off_in_window;
1043*7c478bd9Sstevel@tonic-gate 		copy_bytes = MIN(nbytes, rem_in_window);
1044*7c478bd9Sstevel@tonic-gate 
1045*7c478bd9Sstevel@tonic-gate 		if (reading) {
1046*7c478bd9Sstevel@tonic-gate 			(void) bcopy(raddr, buf_addr, copy_bytes);
1047*7c478bd9Sstevel@tonic-gate 		} else {
1048*7c478bd9Sstevel@tonic-gate 			(void) bcopy(buf_addr, raddr, copy_bytes);
1049*7c478bd9Sstevel@tonic-gate 		}
1050*7c478bd9Sstevel@tonic-gate 		mutex_exit(&rsp->rd_device_lock);
1051*7c478bd9Sstevel@tonic-gate 
1052*7c478bd9Sstevel@tonic-gate 		offset   += copy_bytes;
1053*7c478bd9Sstevel@tonic-gate 		buf_addr += copy_bytes;
1054*7c478bd9Sstevel@tonic-gate 		nbytes   -= copy_bytes;
1055*7c478bd9Sstevel@tonic-gate 	}
1056*7c478bd9Sstevel@tonic-gate }
1057*7c478bd9Sstevel@tonic-gate 
1058*7c478bd9Sstevel@tonic-gate static int
1059*7c478bd9Sstevel@tonic-gate rd_strategy(struct buf *bp)
1060*7c478bd9Sstevel@tonic-gate {
1061*7c478bd9Sstevel@tonic-gate 	rd_devstate_t	*rsp;
1062*7c478bd9Sstevel@tonic-gate 	offset_t	offset;
1063*7c478bd9Sstevel@tonic-gate 
1064*7c478bd9Sstevel@tonic-gate 	rsp = ddi_get_soft_state(rd_statep, getminor(bp->b_edev));
1065*7c478bd9Sstevel@tonic-gate 	offset = bp->b_blkno * DEV_BSIZE;
1066*7c478bd9Sstevel@tonic-gate 
1067*7c478bd9Sstevel@tonic-gate 	if (rsp == NULL) {
1068*7c478bd9Sstevel@tonic-gate 		bp->b_error = ENXIO;
1069*7c478bd9Sstevel@tonic-gate 		bp->b_flags |= B_ERROR;
1070*7c478bd9Sstevel@tonic-gate 	} else if (offset >= rsp->rd_size) {
1071*7c478bd9Sstevel@tonic-gate 		bp->b_error = EINVAL;
1072*7c478bd9Sstevel@tonic-gate 		bp->b_flags |= B_ERROR;
1073*7c478bd9Sstevel@tonic-gate 	} else {
1074*7c478bd9Sstevel@tonic-gate 		size_t	nbytes;
1075*7c478bd9Sstevel@tonic-gate 
1076*7c478bd9Sstevel@tonic-gate 		if (rsp->rd_kstat) {
1077*7c478bd9Sstevel@tonic-gate 			mutex_enter(rsp->rd_kstat->ks_lock);
1078*7c478bd9Sstevel@tonic-gate 			kstat_runq_enter(KSTAT_IO_PTR(rsp->rd_kstat));
1079*7c478bd9Sstevel@tonic-gate 			mutex_exit(rsp->rd_kstat->ks_lock);
1080*7c478bd9Sstevel@tonic-gate 		}
1081*7c478bd9Sstevel@tonic-gate 
1082*7c478bd9Sstevel@tonic-gate 		nbytes = min(bp->b_bcount, rsp->rd_size - offset);
1083*7c478bd9Sstevel@tonic-gate 
1084*7c478bd9Sstevel@tonic-gate 		rd_rw(rsp, bp, offset, nbytes);
1085*7c478bd9Sstevel@tonic-gate 
1086*7c478bd9Sstevel@tonic-gate 		bp->b_resid = bp->b_bcount - nbytes;
1087*7c478bd9Sstevel@tonic-gate 
1088*7c478bd9Sstevel@tonic-gate 		if (rsp->rd_kstat) {
1089*7c478bd9Sstevel@tonic-gate 			kstat_io_t *kioptr;
1090*7c478bd9Sstevel@tonic-gate 
1091*7c478bd9Sstevel@tonic-gate 			mutex_enter(rsp->rd_kstat->ks_lock);
1092*7c478bd9Sstevel@tonic-gate 			kioptr = KSTAT_IO_PTR(rsp->rd_kstat);
1093*7c478bd9Sstevel@tonic-gate 			if (bp->b_flags & B_READ) {
1094*7c478bd9Sstevel@tonic-gate 				kioptr->nread += nbytes;
1095*7c478bd9Sstevel@tonic-gate 				kioptr->reads++;
1096*7c478bd9Sstevel@tonic-gate 			} else {
1097*7c478bd9Sstevel@tonic-gate 				kioptr->nwritten += nbytes;
1098*7c478bd9Sstevel@tonic-gate 				kioptr->writes++;
1099*7c478bd9Sstevel@tonic-gate 			}
1100*7c478bd9Sstevel@tonic-gate 			kstat_runq_exit(kioptr);
1101*7c478bd9Sstevel@tonic-gate 			mutex_exit(rsp->rd_kstat->ks_lock);
1102*7c478bd9Sstevel@tonic-gate 		}
1103*7c478bd9Sstevel@tonic-gate 	}
1104*7c478bd9Sstevel@tonic-gate 
1105*7c478bd9Sstevel@tonic-gate 	biodone(bp);
1106*7c478bd9Sstevel@tonic-gate 	return (0);
1107*7c478bd9Sstevel@tonic-gate }
1108*7c478bd9Sstevel@tonic-gate 
1109*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1110*7c478bd9Sstevel@tonic-gate static int
1111*7c478bd9Sstevel@tonic-gate rd_read(dev_t dev, struct uio *uiop, cred_t *credp)
1112*7c478bd9Sstevel@tonic-gate {
1113*7c478bd9Sstevel@tonic-gate 	rd_devstate_t	*rsp;
1114*7c478bd9Sstevel@tonic-gate 
1115*7c478bd9Sstevel@tonic-gate 	rsp = ddi_get_soft_state(rd_statep, getminor(dev));
1116*7c478bd9Sstevel@tonic-gate 
1117*7c478bd9Sstevel@tonic-gate 	if (uiop->uio_offset >= rsp->rd_size)
1118*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
1119*7c478bd9Sstevel@tonic-gate 
1120*7c478bd9Sstevel@tonic-gate 	return (physio(rd_strategy, NULL, dev, B_READ, rd_minphys, uiop));
1121*7c478bd9Sstevel@tonic-gate }
1122*7c478bd9Sstevel@tonic-gate 
1123*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1124*7c478bd9Sstevel@tonic-gate static int
1125*7c478bd9Sstevel@tonic-gate rd_write(dev_t dev, register struct uio *uiop, cred_t *credp)
1126*7c478bd9Sstevel@tonic-gate {
1127*7c478bd9Sstevel@tonic-gate 	rd_devstate_t	*rsp;
1128*7c478bd9Sstevel@tonic-gate 
1129*7c478bd9Sstevel@tonic-gate 	rsp = ddi_get_soft_state(rd_statep, getminor(dev));
1130*7c478bd9Sstevel@tonic-gate 
1131*7c478bd9Sstevel@tonic-gate 	if (uiop->uio_offset >= rsp->rd_size)
1132*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
1133*7c478bd9Sstevel@tonic-gate 
1134*7c478bd9Sstevel@tonic-gate 	return (physio(rd_strategy, NULL, dev, B_WRITE, rd_minphys, uiop));
1135*7c478bd9Sstevel@tonic-gate }
1136*7c478bd9Sstevel@tonic-gate 
1137*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1138*7c478bd9Sstevel@tonic-gate static int
1139*7c478bd9Sstevel@tonic-gate rd_create_disk(dev_t dev, struct rd_ioctl *urip, int mode, int *rvalp)
1140*7c478bd9Sstevel@tonic-gate {
1141*7c478bd9Sstevel@tonic-gate 	struct rd_ioctl	kri;
1142*7c478bd9Sstevel@tonic-gate 	size_t		size;
1143*7c478bd9Sstevel@tonic-gate 	rd_devstate_t	*rsp;
1144*7c478bd9Sstevel@tonic-gate 
1145*7c478bd9Sstevel@tonic-gate 	if (ddi_copyin(urip, &kri, sizeof (kri), mode) == -1) {
1146*7c478bd9Sstevel@tonic-gate 		return (EFAULT);
1147*7c478bd9Sstevel@tonic-gate 	}
1148*7c478bd9Sstevel@tonic-gate 
1149*7c478bd9Sstevel@tonic-gate 	kri.ri_name[RD_NAME_LEN] = '\0';
1150*7c478bd9Sstevel@tonic-gate 
1151*7c478bd9Sstevel@tonic-gate 	size = kri.ri_size;
1152*7c478bd9Sstevel@tonic-gate 	if (size == 0) {
1153*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
1154*7c478bd9Sstevel@tonic-gate 	}
1155*7c478bd9Sstevel@tonic-gate 	size = ptob(btopr(size));
1156*7c478bd9Sstevel@tonic-gate 
1157*7c478bd9Sstevel@tonic-gate 	mutex_enter(&rd_lock);
1158*7c478bd9Sstevel@tonic-gate 
1159*7c478bd9Sstevel@tonic-gate 	if (rd_find_named_disk(kri.ri_name) != NULL) {
1160*7c478bd9Sstevel@tonic-gate 		mutex_exit(&rd_lock);
1161*7c478bd9Sstevel@tonic-gate 		return (EEXIST);
1162*7c478bd9Sstevel@tonic-gate 	}
1163*7c478bd9Sstevel@tonic-gate 
1164*7c478bd9Sstevel@tonic-gate 	rsp = rd_alloc_resources(kri.ri_name, size, rd_dip);
1165*7c478bd9Sstevel@tonic-gate 	if (rsp == NULL) {
1166*7c478bd9Sstevel@tonic-gate 		mutex_exit(&rd_lock);
1167*7c478bd9Sstevel@tonic-gate 		return (EAGAIN);
1168*7c478bd9Sstevel@tonic-gate 	}
1169*7c478bd9Sstevel@tonic-gate 
1170*7c478bd9Sstevel@tonic-gate 	mutex_exit(&rd_lock);
1171*7c478bd9Sstevel@tonic-gate 
1172*7c478bd9Sstevel@tonic-gate 	return (ddi_copyout(&kri, urip, sizeof (kri), mode) == -1 ? EFAULT : 0);
1173*7c478bd9Sstevel@tonic-gate }
1174*7c478bd9Sstevel@tonic-gate 
1175*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1176*7c478bd9Sstevel@tonic-gate static int
1177*7c478bd9Sstevel@tonic-gate rd_delete_disk(dev_t dev, struct rd_ioctl *urip, int mode)
1178*7c478bd9Sstevel@tonic-gate {
1179*7c478bd9Sstevel@tonic-gate 	struct rd_ioctl	kri;
1180*7c478bd9Sstevel@tonic-gate 	rd_devstate_t	*rsp;
1181*7c478bd9Sstevel@tonic-gate 
1182*7c478bd9Sstevel@tonic-gate 	if (ddi_copyin(urip, &kri, sizeof (kri), mode) == -1) {
1183*7c478bd9Sstevel@tonic-gate 		return (EFAULT);
1184*7c478bd9Sstevel@tonic-gate 	}
1185*7c478bd9Sstevel@tonic-gate 
1186*7c478bd9Sstevel@tonic-gate 	kri.ri_name[RD_NAME_LEN] = '\0';
1187*7c478bd9Sstevel@tonic-gate 
1188*7c478bd9Sstevel@tonic-gate 	mutex_enter(&rd_lock);
1189*7c478bd9Sstevel@tonic-gate 
1190*7c478bd9Sstevel@tonic-gate 	rsp = rd_find_named_disk(kri.ri_name);
1191*7c478bd9Sstevel@tonic-gate 	if (rsp == NULL || rsp->rd_dip != rd_dip) {
1192*7c478bd9Sstevel@tonic-gate 		mutex_exit(&rd_lock);
1193*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
1194*7c478bd9Sstevel@tonic-gate 	}
1195*7c478bd9Sstevel@tonic-gate 	if (rd_is_open(rsp)) {
1196*7c478bd9Sstevel@tonic-gate 		mutex_exit(&rd_lock);
1197*7c478bd9Sstevel@tonic-gate 		return (EBUSY);
1198*7c478bd9Sstevel@tonic-gate 	}
1199*7c478bd9Sstevel@tonic-gate 
1200*7c478bd9Sstevel@tonic-gate 	rd_dealloc_resources(rsp);
1201*7c478bd9Sstevel@tonic-gate 
1202*7c478bd9Sstevel@tonic-gate 	mutex_exit(&rd_lock);
1203*7c478bd9Sstevel@tonic-gate 
1204*7c478bd9Sstevel@tonic-gate 	return (0);
1205*7c478bd9Sstevel@tonic-gate }
1206*7c478bd9Sstevel@tonic-gate 
1207*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1208*7c478bd9Sstevel@tonic-gate static int
1209*7c478bd9Sstevel@tonic-gate rd_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
1210*7c478bd9Sstevel@tonic-gate {
1211*7c478bd9Sstevel@tonic-gate 	minor_t		minor;
1212*7c478bd9Sstevel@tonic-gate 	int		error;
1213*7c478bd9Sstevel@tonic-gate 	enum dkio_state	dkstate;
1214*7c478bd9Sstevel@tonic-gate 	rd_devstate_t	*rsp;
1215*7c478bd9Sstevel@tonic-gate 
1216*7c478bd9Sstevel@tonic-gate 	minor = getminor(dev);
1217*7c478bd9Sstevel@tonic-gate 
1218*7c478bd9Sstevel@tonic-gate 	/*
1219*7c478bd9Sstevel@tonic-gate 	 * Ramdisk ioctls only apply to the master device.
1220*7c478bd9Sstevel@tonic-gate 	 */
1221*7c478bd9Sstevel@tonic-gate 	if (minor == RD_CTL_MINOR) {
1222*7c478bd9Sstevel@tonic-gate 		struct rd_ioctl *rip = (struct rd_ioctl *)arg;
1223*7c478bd9Sstevel@tonic-gate 
1224*7c478bd9Sstevel@tonic-gate 		/*
1225*7c478bd9Sstevel@tonic-gate 		 * The query commands only need read-access - i.e., normal
1226*7c478bd9Sstevel@tonic-gate 		 * users are allowed to do those on the controlling device
1227*7c478bd9Sstevel@tonic-gate 		 * as long as they can open it read-only.
1228*7c478bd9Sstevel@tonic-gate 		 */
1229*7c478bd9Sstevel@tonic-gate 		switch (cmd) {
1230*7c478bd9Sstevel@tonic-gate 		case RD_CREATE_DISK:
1231*7c478bd9Sstevel@tonic-gate 			if ((mode & FWRITE) == 0)
1232*7c478bd9Sstevel@tonic-gate 				return (EPERM);
1233*7c478bd9Sstevel@tonic-gate 			return (rd_create_disk(dev, rip, mode, rvalp));
1234*7c478bd9Sstevel@tonic-gate 
1235*7c478bd9Sstevel@tonic-gate 		case RD_DELETE_DISK:
1236*7c478bd9Sstevel@tonic-gate 			if ((mode & FWRITE) == 0)
1237*7c478bd9Sstevel@tonic-gate 				return (EPERM);
1238*7c478bd9Sstevel@tonic-gate 			return (rd_delete_disk(dev, rip, mode));
1239*7c478bd9Sstevel@tonic-gate 
1240*7c478bd9Sstevel@tonic-gate 		default:
1241*7c478bd9Sstevel@tonic-gate 			return (EINVAL);
1242*7c478bd9Sstevel@tonic-gate 		}
1243*7c478bd9Sstevel@tonic-gate 	}
1244*7c478bd9Sstevel@tonic-gate 
1245*7c478bd9Sstevel@tonic-gate 	rsp = ddi_get_soft_state(rd_statep, minor);
1246*7c478bd9Sstevel@tonic-gate 	if (rsp == NULL) {
1247*7c478bd9Sstevel@tonic-gate 		return (ENXIO);
1248*7c478bd9Sstevel@tonic-gate 	}
1249*7c478bd9Sstevel@tonic-gate 
1250*7c478bd9Sstevel@tonic-gate 	/*
1251*7c478bd9Sstevel@tonic-gate 	 * These are for faking out utilities like newfs.
1252*7c478bd9Sstevel@tonic-gate 	 */
1253*7c478bd9Sstevel@tonic-gate 	switch (cmd) {
1254*7c478bd9Sstevel@tonic-gate 	case VOLIOCINFO:
1255*7c478bd9Sstevel@tonic-gate 		/* pcfs does this to see if it needs to set PCFS_NOCHK */
1256*7c478bd9Sstevel@tonic-gate 		/* 0 means it should set it */
1257*7c478bd9Sstevel@tonic-gate 		return (0);
1258*7c478bd9Sstevel@tonic-gate 	case DKIOCGVTOC:
1259*7c478bd9Sstevel@tonic-gate 		switch (ddi_model_convert_from(mode & FMODELS)) {
1260*7c478bd9Sstevel@tonic-gate 		case DDI_MODEL_ILP32: {
1261*7c478bd9Sstevel@tonic-gate 			struct vtoc32 vtoc32;
1262*7c478bd9Sstevel@tonic-gate 
1263*7c478bd9Sstevel@tonic-gate 			vtoctovtoc32(rsp->rd_vtoc, vtoc32);
1264*7c478bd9Sstevel@tonic-gate 			if (ddi_copyout(&vtoc32, (void *)arg,
1265*7c478bd9Sstevel@tonic-gate 			    sizeof (struct vtoc32), mode))
1266*7c478bd9Sstevel@tonic-gate 				return (EFAULT);
1267*7c478bd9Sstevel@tonic-gate 			}
1268*7c478bd9Sstevel@tonic-gate 			break;
1269*7c478bd9Sstevel@tonic-gate 
1270*7c478bd9Sstevel@tonic-gate 		case DDI_MODEL_NONE:
1271*7c478bd9Sstevel@tonic-gate 			if (ddi_copyout(&rsp->rd_vtoc, (void *)arg,
1272*7c478bd9Sstevel@tonic-gate 			    sizeof (struct vtoc), mode))
1273*7c478bd9Sstevel@tonic-gate 				return (EFAULT);
1274*7c478bd9Sstevel@tonic-gate 			break;
1275*7c478bd9Sstevel@tonic-gate 		}
1276*7c478bd9Sstevel@tonic-gate 		return (0);
1277*7c478bd9Sstevel@tonic-gate 	case DKIOCINFO:
1278*7c478bd9Sstevel@tonic-gate 		error = ddi_copyout(&rsp->rd_ci, (void *)arg,
1279*7c478bd9Sstevel@tonic-gate 		    sizeof (struct dk_cinfo), mode);
1280*7c478bd9Sstevel@tonic-gate 		if (error)
1281*7c478bd9Sstevel@tonic-gate 			return (EFAULT);
1282*7c478bd9Sstevel@tonic-gate 		return (0);
1283*7c478bd9Sstevel@tonic-gate 	case DKIOCG_VIRTGEOM:
1284*7c478bd9Sstevel@tonic-gate 	case DKIOCG_PHYGEOM:
1285*7c478bd9Sstevel@tonic-gate 	case DKIOCGGEOM:
1286*7c478bd9Sstevel@tonic-gate 		error = ddi_copyout(&rsp->rd_dkg, (void *)arg,
1287*7c478bd9Sstevel@tonic-gate 		    sizeof (struct dk_geom), mode);
1288*7c478bd9Sstevel@tonic-gate 		if (error)
1289*7c478bd9Sstevel@tonic-gate 			return (EFAULT);
1290*7c478bd9Sstevel@tonic-gate 		return (0);
1291*7c478bd9Sstevel@tonic-gate 	case DKIOCSTATE:
1292*7c478bd9Sstevel@tonic-gate 		/* the file is always there */
1293*7c478bd9Sstevel@tonic-gate 		dkstate = DKIO_INSERTED;
1294*7c478bd9Sstevel@tonic-gate 		error = ddi_copyout(&dkstate, (void *)arg,
1295*7c478bd9Sstevel@tonic-gate 		    sizeof (enum dkio_state), mode);
1296*7c478bd9Sstevel@tonic-gate 		if (error)
1297*7c478bd9Sstevel@tonic-gate 			return (EFAULT);
1298*7c478bd9Sstevel@tonic-gate 		return (0);
1299*7c478bd9Sstevel@tonic-gate 	default:
1300*7c478bd9Sstevel@tonic-gate 		return (ENOTTY);
1301*7c478bd9Sstevel@tonic-gate 	}
1302*7c478bd9Sstevel@tonic-gate }
1303*7c478bd9Sstevel@tonic-gate 
1304*7c478bd9Sstevel@tonic-gate 
1305*7c478bd9Sstevel@tonic-gate static struct cb_ops rd_cb_ops = {
1306*7c478bd9Sstevel@tonic-gate 	rd_open,
1307*7c478bd9Sstevel@tonic-gate 	rd_close,
1308*7c478bd9Sstevel@tonic-gate 	rd_strategy,
1309*7c478bd9Sstevel@tonic-gate 	nodev,
1310*7c478bd9Sstevel@tonic-gate 	nodev,		/* dump */
1311*7c478bd9Sstevel@tonic-gate 	rd_read,
1312*7c478bd9Sstevel@tonic-gate 	rd_write,
1313*7c478bd9Sstevel@tonic-gate 	rd_ioctl,
1314*7c478bd9Sstevel@tonic-gate 	nodev,		/* devmap */
1315*7c478bd9Sstevel@tonic-gate 	nodev,		/* mmap */
1316*7c478bd9Sstevel@tonic-gate 	nodev,		/* segmap */
1317*7c478bd9Sstevel@tonic-gate 	nochpoll,	/* poll */
1318*7c478bd9Sstevel@tonic-gate 	ddi_prop_op,
1319*7c478bd9Sstevel@tonic-gate 	NULL,
1320*7c478bd9Sstevel@tonic-gate 	D_NEW | D_MP
1321*7c478bd9Sstevel@tonic-gate };
1322*7c478bd9Sstevel@tonic-gate 
1323*7c478bd9Sstevel@tonic-gate static struct dev_ops rd_ops = {
1324*7c478bd9Sstevel@tonic-gate 	DEVO_REV,
1325*7c478bd9Sstevel@tonic-gate 	0,
1326*7c478bd9Sstevel@tonic-gate 	rd_getinfo,
1327*7c478bd9Sstevel@tonic-gate 	nulldev,	/* identify */
1328*7c478bd9Sstevel@tonic-gate 	nulldev,	/* probe */
1329*7c478bd9Sstevel@tonic-gate 	rd_attach,
1330*7c478bd9Sstevel@tonic-gate 	rd_detach,
1331*7c478bd9Sstevel@tonic-gate 	nodev,		/* reset */
1332*7c478bd9Sstevel@tonic-gate 	&rd_cb_ops,
1333*7c478bd9Sstevel@tonic-gate 	(struct bus_ops *)0
1334*7c478bd9Sstevel@tonic-gate };
1335*7c478bd9Sstevel@tonic-gate 
1336*7c478bd9Sstevel@tonic-gate 
1337*7c478bd9Sstevel@tonic-gate extern struct mod_ops mod_driverops;
1338*7c478bd9Sstevel@tonic-gate 
1339*7c478bd9Sstevel@tonic-gate static struct modldrv modldrv = {
1340*7c478bd9Sstevel@tonic-gate 	&mod_driverops,
1341*7c478bd9Sstevel@tonic-gate 	"ramdisk driver v%I%",
1342*7c478bd9Sstevel@tonic-gate 	&rd_ops
1343*7c478bd9Sstevel@tonic-gate };
1344*7c478bd9Sstevel@tonic-gate 
1345*7c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
1346*7c478bd9Sstevel@tonic-gate 	MODREV_1,
1347*7c478bd9Sstevel@tonic-gate 	&modldrv,
1348*7c478bd9Sstevel@tonic-gate 	0
1349*7c478bd9Sstevel@tonic-gate };
1350*7c478bd9Sstevel@tonic-gate 
1351*7c478bd9Sstevel@tonic-gate int
1352*7c478bd9Sstevel@tonic-gate _init(void)
1353*7c478bd9Sstevel@tonic-gate {
1354*7c478bd9Sstevel@tonic-gate 	int e;
1355*7c478bd9Sstevel@tonic-gate 
1356*7c478bd9Sstevel@tonic-gate 	if ((e = ddi_soft_state_init(&rd_statep,
1357*7c478bd9Sstevel@tonic-gate 	    sizeof (rd_devstate_t), 0)) != 0) {
1358*7c478bd9Sstevel@tonic-gate 		return (e);
1359*7c478bd9Sstevel@tonic-gate 	}
1360*7c478bd9Sstevel@tonic-gate 
1361*7c478bd9Sstevel@tonic-gate 	mutex_init(&rd_lock, NULL, MUTEX_DRIVER, NULL);
1362*7c478bd9Sstevel@tonic-gate 
1363*7c478bd9Sstevel@tonic-gate 	if ((e = mod_install(&modlinkage)) != 0)  {
1364*7c478bd9Sstevel@tonic-gate 		mutex_destroy(&rd_lock);
1365*7c478bd9Sstevel@tonic-gate 		ddi_soft_state_fini(&rd_statep);
1366*7c478bd9Sstevel@tonic-gate 	}
1367*7c478bd9Sstevel@tonic-gate 
1368*7c478bd9Sstevel@tonic-gate 	return (e);
1369*7c478bd9Sstevel@tonic-gate }
1370*7c478bd9Sstevel@tonic-gate 
1371*7c478bd9Sstevel@tonic-gate int
1372*7c478bd9Sstevel@tonic-gate _fini(void)
1373*7c478bd9Sstevel@tonic-gate {
1374*7c478bd9Sstevel@tonic-gate 	int e;
1375*7c478bd9Sstevel@tonic-gate 
1376*7c478bd9Sstevel@tonic-gate 	if ((e = mod_remove(&modlinkage)) != 0)  {
1377*7c478bd9Sstevel@tonic-gate 		return (e);
1378*7c478bd9Sstevel@tonic-gate 	}
1379*7c478bd9Sstevel@tonic-gate 
1380*7c478bd9Sstevel@tonic-gate 	ddi_soft_state_fini(&rd_statep);
1381*7c478bd9Sstevel@tonic-gate 	mutex_destroy(&rd_lock);
1382*7c478bd9Sstevel@tonic-gate 
1383*7c478bd9Sstevel@tonic-gate 	return (e);
1384*7c478bd9Sstevel@tonic-gate }
1385*7c478bd9Sstevel@tonic-gate 
1386*7c478bd9Sstevel@tonic-gate int
1387*7c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
1388*7c478bd9Sstevel@tonic-gate {
1389*7c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
1390*7c478bd9Sstevel@tonic-gate }
1391