1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28*0Sstevel@tonic-gate /*	  All Rights Reserved  	*/
29*0Sstevel@tonic-gate 
30*0Sstevel@tonic-gate /*
31*0Sstevel@tonic-gate  * University Copyright- Copyright (c) 1982, 1986, 1988
32*0Sstevel@tonic-gate  * The Regents of the University of California
33*0Sstevel@tonic-gate  * All Rights Reserved
34*0Sstevel@tonic-gate  *
35*0Sstevel@tonic-gate  * University Acknowledgment- Portions of this document are derived from
36*0Sstevel@tonic-gate  * software developed by the University of California, Berkeley, and its
37*0Sstevel@tonic-gate  * contributors.
38*0Sstevel@tonic-gate  */
39*0Sstevel@tonic-gate 
40*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
41*0Sstevel@tonic-gate 
42*0Sstevel@tonic-gate /*
43*0Sstevel@tonic-gate  * VM - segment of a mapped device.
44*0Sstevel@tonic-gate  *
45*0Sstevel@tonic-gate  * This segment driver is used when mapping character special devices.
46*0Sstevel@tonic-gate  */
47*0Sstevel@tonic-gate 
48*0Sstevel@tonic-gate #include <sys/types.h>
49*0Sstevel@tonic-gate #include <sys/t_lock.h>
50*0Sstevel@tonic-gate #include <sys/sysmacros.h>
51*0Sstevel@tonic-gate #include <sys/vtrace.h>
52*0Sstevel@tonic-gate #include <sys/systm.h>
53*0Sstevel@tonic-gate #include <sys/vmsystm.h>
54*0Sstevel@tonic-gate #include <sys/mman.h>
55*0Sstevel@tonic-gate #include <sys/errno.h>
56*0Sstevel@tonic-gate #include <sys/kmem.h>
57*0Sstevel@tonic-gate #include <sys/cmn_err.h>
58*0Sstevel@tonic-gate #include <sys/vnode.h>
59*0Sstevel@tonic-gate #include <sys/proc.h>
60*0Sstevel@tonic-gate #include <sys/conf.h>
61*0Sstevel@tonic-gate #include <sys/debug.h>
62*0Sstevel@tonic-gate #include <sys/ddidevmap.h>
63*0Sstevel@tonic-gate #include <sys/lgrp.h>
64*0Sstevel@tonic-gate 
65*0Sstevel@tonic-gate #include <vm/page.h>
66*0Sstevel@tonic-gate #include <vm/hat.h>
67*0Sstevel@tonic-gate #include <vm/as.h>
68*0Sstevel@tonic-gate #include <vm/seg.h>
69*0Sstevel@tonic-gate #include <vm/seg_dev.h>
70*0Sstevel@tonic-gate #include <vm/seg_kp.h>
71*0Sstevel@tonic-gate #include <vm/seg_kmem.h>
72*0Sstevel@tonic-gate #include <vm/vpage.h>
73*0Sstevel@tonic-gate 
74*0Sstevel@tonic-gate #include <sys/sunddi.h>
75*0Sstevel@tonic-gate #include <sys/esunddi.h>
76*0Sstevel@tonic-gate #include <sys/fs/snode.h>
77*0Sstevel@tonic-gate 
78*0Sstevel@tonic-gate #if DEBUG
79*0Sstevel@tonic-gate int segdev_debug;
80*0Sstevel@tonic-gate #define	DEBUGF(level, args) { if (segdev_debug >= (level)) cmn_err args; }
81*0Sstevel@tonic-gate #else
82*0Sstevel@tonic-gate #define	DEBUGF(level, args)
83*0Sstevel@tonic-gate #endif
84*0Sstevel@tonic-gate 
85*0Sstevel@tonic-gate /* Default timeout for devmap context management */
86*0Sstevel@tonic-gate #define	CTX_TIMEOUT_VALUE 0
87*0Sstevel@tonic-gate 
88*0Sstevel@tonic-gate #define	HOLD_DHP_LOCK(dhp)  if (dhp->dh_flags & DEVMAP_ALLOW_REMAP) \
89*0Sstevel@tonic-gate 			{ mutex_enter(&dhp->dh_lock); }
90*0Sstevel@tonic-gate 
91*0Sstevel@tonic-gate #define	RELE_DHP_LOCK(dhp) if (dhp->dh_flags & DEVMAP_ALLOW_REMAP) \
92*0Sstevel@tonic-gate 			{ mutex_exit(&dhp->dh_lock); }
93*0Sstevel@tonic-gate 
94*0Sstevel@tonic-gate #define	round_down_p2(a, s)	((a) & ~((s) - 1))
95*0Sstevel@tonic-gate #define	round_up_p2(a, s)	(((a) + (s) - 1) & ~((s) - 1))
96*0Sstevel@tonic-gate 
97*0Sstevel@tonic-gate /*
98*0Sstevel@tonic-gate  * VA_PA_ALIGNED checks to see if both VA and PA are on pgsize boundary
99*0Sstevel@tonic-gate  * VA_PA_PGSIZE_ALIGNED check to see if VA is aligned with PA w.r.t. pgsize
100*0Sstevel@tonic-gate  */
101*0Sstevel@tonic-gate #define	VA_PA_ALIGNED(uvaddr, paddr, pgsize)		\
102*0Sstevel@tonic-gate 	(((uvaddr | paddr) & (pgsize - 1)) == 0)
103*0Sstevel@tonic-gate #define	VA_PA_PGSIZE_ALIGNED(uvaddr, paddr, pgsize)	\
104*0Sstevel@tonic-gate 	(((uvaddr ^ paddr) & (pgsize - 1)) == 0)
105*0Sstevel@tonic-gate 
106*0Sstevel@tonic-gate #define	vpgtob(n)	((n) * sizeof (struct vpage))	/* For brevity */
107*0Sstevel@tonic-gate 
108*0Sstevel@tonic-gate #define	VTOCVP(vp)	(VTOS(vp)->s_commonvp)	/* we "know" it's an snode */
109*0Sstevel@tonic-gate 
110*0Sstevel@tonic-gate static struct devmap_ctx *devmapctx_list = NULL;
111*0Sstevel@tonic-gate static struct devmap_softlock *devmap_slist = NULL;
112*0Sstevel@tonic-gate 
113*0Sstevel@tonic-gate /*
114*0Sstevel@tonic-gate  * mutex, vnode and page for the page of zeros we use for the trash mappings.
115*0Sstevel@tonic-gate  * One trash page is allocated on the first ddi_umem_setup call that uses it
116*0Sstevel@tonic-gate  * XXX Eventually, we may want to combine this with what segnf does when all
117*0Sstevel@tonic-gate  * hat layers implement HAT_NOFAULT.
118*0Sstevel@tonic-gate  *
119*0Sstevel@tonic-gate  * The trash page is used when the backing store for a userland mapping is
120*0Sstevel@tonic-gate  * removed but the application semantics do not take kindly to a SIGBUS.
121*0Sstevel@tonic-gate  * In that scenario, the applications pages are mapped to some dummy page
122*0Sstevel@tonic-gate  * which returns garbage on read and writes go into a common place.
123*0Sstevel@tonic-gate  * (Perfect for NO_FAULT semantics)
124*0Sstevel@tonic-gate  * The device driver is responsible to communicating to the app with some
125*0Sstevel@tonic-gate  * other mechanism that such remapping has happened and the app should take
126*0Sstevel@tonic-gate  * corrective action.
127*0Sstevel@tonic-gate  * We can also use an anonymous memory page as there is no requirement to
128*0Sstevel@tonic-gate  * keep the page locked, however this complicates the fault code. RFE.
129*0Sstevel@tonic-gate  */
130*0Sstevel@tonic-gate static struct vnode trashvp;
131*0Sstevel@tonic-gate static struct page *trashpp;
132*0Sstevel@tonic-gate 
133*0Sstevel@tonic-gate /* Non-pageable kernel memory is allocated from the umem_np_arena. */
134*0Sstevel@tonic-gate static vmem_t *umem_np_arena;
135*0Sstevel@tonic-gate 
136*0Sstevel@tonic-gate /* Set the cookie to a value we know will never be a valid umem_cookie */
137*0Sstevel@tonic-gate #define	DEVMAP_DEVMEM_COOKIE	((ddi_umem_cookie_t)0x1)
138*0Sstevel@tonic-gate 
139*0Sstevel@tonic-gate /*
140*0Sstevel@tonic-gate  * Macros to check if type of devmap handle
141*0Sstevel@tonic-gate  */
142*0Sstevel@tonic-gate #define	cookie_is_devmem(c)	\
143*0Sstevel@tonic-gate 	((c) == (struct ddi_umem_cookie *)DEVMAP_DEVMEM_COOKIE)
144*0Sstevel@tonic-gate 
145*0Sstevel@tonic-gate #define	cookie_is_pmem(c)	\
146*0Sstevel@tonic-gate 	((c) == (struct ddi_umem_cookie *)DEVMAP_PMEM_COOKIE)
147*0Sstevel@tonic-gate 
148*0Sstevel@tonic-gate #define	cookie_is_kpmem(c)	(!cookie_is_devmem(c) && !cookie_is_pmem(c) &&\
149*0Sstevel@tonic-gate 	((c)->type == KMEM_PAGEABLE))
150*0Sstevel@tonic-gate 
151*0Sstevel@tonic-gate #define	dhp_is_devmem(dhp)	\
152*0Sstevel@tonic-gate 	(cookie_is_devmem((struct ddi_umem_cookie *)((dhp)->dh_cookie)))
153*0Sstevel@tonic-gate 
154*0Sstevel@tonic-gate #define	dhp_is_pmem(dhp)	\
155*0Sstevel@tonic-gate 	(cookie_is_pmem((struct ddi_umem_cookie *)((dhp)->dh_cookie)))
156*0Sstevel@tonic-gate 
157*0Sstevel@tonic-gate #define	dhp_is_kpmem(dhp)	\
158*0Sstevel@tonic-gate 	(cookie_is_kpmem((struct ddi_umem_cookie *)((dhp)->dh_cookie)))
159*0Sstevel@tonic-gate 
160*0Sstevel@tonic-gate /*
161*0Sstevel@tonic-gate  * Private seg op routines.
162*0Sstevel@tonic-gate  */
163*0Sstevel@tonic-gate static int	segdev_dup(struct seg *, struct seg *);
164*0Sstevel@tonic-gate static int	segdev_unmap(struct seg *, caddr_t, size_t);
165*0Sstevel@tonic-gate static void	segdev_free(struct seg *);
166*0Sstevel@tonic-gate static faultcode_t segdev_fault(struct hat *, struct seg *, caddr_t, size_t,
167*0Sstevel@tonic-gate 		    enum fault_type, enum seg_rw);
168*0Sstevel@tonic-gate static faultcode_t segdev_faulta(struct seg *, caddr_t);
169*0Sstevel@tonic-gate static int	segdev_setprot(struct seg *, caddr_t, size_t, uint_t);
170*0Sstevel@tonic-gate static int	segdev_checkprot(struct seg *, caddr_t, size_t, uint_t);
171*0Sstevel@tonic-gate static void	segdev_badop(void);
172*0Sstevel@tonic-gate static int	segdev_sync(struct seg *, caddr_t, size_t, int, uint_t);
173*0Sstevel@tonic-gate static size_t	segdev_incore(struct seg *, caddr_t, size_t, char *);
174*0Sstevel@tonic-gate static int	segdev_lockop(struct seg *, caddr_t, size_t, int, int,
175*0Sstevel@tonic-gate 		    ulong_t *, size_t);
176*0Sstevel@tonic-gate static int	segdev_getprot(struct seg *, caddr_t, size_t, uint_t *);
177*0Sstevel@tonic-gate static u_offset_t	segdev_getoffset(struct seg *, caddr_t);
178*0Sstevel@tonic-gate static int	segdev_gettype(struct seg *, caddr_t);
179*0Sstevel@tonic-gate static int	segdev_getvp(struct seg *, caddr_t, struct vnode **);
180*0Sstevel@tonic-gate static int	segdev_advise(struct seg *, caddr_t, size_t, uint_t);
181*0Sstevel@tonic-gate static void	segdev_dump(struct seg *);
182*0Sstevel@tonic-gate static int	segdev_pagelock(struct seg *, caddr_t, size_t,
183*0Sstevel@tonic-gate 		    struct page ***, enum lock_type, enum seg_rw);
184*0Sstevel@tonic-gate static int	segdev_setpagesize(struct seg *, caddr_t, size_t, uint_t);
185*0Sstevel@tonic-gate static int	segdev_getmemid(struct seg *, caddr_t, memid_t *);
186*0Sstevel@tonic-gate static lgrp_mem_policy_info_t	*segdev_getpolicy(struct seg *, caddr_t);
187*0Sstevel@tonic-gate 
188*0Sstevel@tonic-gate /*
189*0Sstevel@tonic-gate  * XXX	this struct is used by rootnex_map_fault to identify
190*0Sstevel@tonic-gate  *	the segment it has been passed. So if you make it
191*0Sstevel@tonic-gate  *	"static" you'll need to fix rootnex_map_fault.
192*0Sstevel@tonic-gate  */
193*0Sstevel@tonic-gate struct seg_ops segdev_ops = {
194*0Sstevel@tonic-gate 	segdev_dup,
195*0Sstevel@tonic-gate 	segdev_unmap,
196*0Sstevel@tonic-gate 	segdev_free,
197*0Sstevel@tonic-gate 	segdev_fault,
198*0Sstevel@tonic-gate 	segdev_faulta,
199*0Sstevel@tonic-gate 	segdev_setprot,
200*0Sstevel@tonic-gate 	segdev_checkprot,
201*0Sstevel@tonic-gate 	(int (*)())segdev_badop,	/* kluster */
202*0Sstevel@tonic-gate 	(size_t (*)(struct seg *))NULL,	/* swapout */
203*0Sstevel@tonic-gate 	segdev_sync,			/* sync */
204*0Sstevel@tonic-gate 	segdev_incore,
205*0Sstevel@tonic-gate 	segdev_lockop,			/* lockop */
206*0Sstevel@tonic-gate 	segdev_getprot,
207*0Sstevel@tonic-gate 	segdev_getoffset,
208*0Sstevel@tonic-gate 	segdev_gettype,
209*0Sstevel@tonic-gate 	segdev_getvp,
210*0Sstevel@tonic-gate 	segdev_advise,
211*0Sstevel@tonic-gate 	segdev_dump,
212*0Sstevel@tonic-gate 	segdev_pagelock,
213*0Sstevel@tonic-gate 	segdev_setpagesize,
214*0Sstevel@tonic-gate 	segdev_getmemid,
215*0Sstevel@tonic-gate 	segdev_getpolicy,
216*0Sstevel@tonic-gate };
217*0Sstevel@tonic-gate 
218*0Sstevel@tonic-gate /*
219*0Sstevel@tonic-gate  * Private segdev support routines
220*0Sstevel@tonic-gate  */
221*0Sstevel@tonic-gate static struct segdev_data *sdp_alloc(void);
222*0Sstevel@tonic-gate 
223*0Sstevel@tonic-gate static void segdev_softunlock(struct hat *, struct seg *, caddr_t,
224*0Sstevel@tonic-gate     size_t, enum seg_rw);
225*0Sstevel@tonic-gate 
226*0Sstevel@tonic-gate static faultcode_t segdev_faultpage(struct hat *, struct seg *, caddr_t,
227*0Sstevel@tonic-gate     struct vpage *, enum fault_type, enum seg_rw, devmap_handle_t *);
228*0Sstevel@tonic-gate 
229*0Sstevel@tonic-gate static faultcode_t segdev_faultpages(struct hat *, struct seg *, caddr_t,
230*0Sstevel@tonic-gate     size_t, enum fault_type, enum seg_rw, devmap_handle_t *);
231*0Sstevel@tonic-gate 
232*0Sstevel@tonic-gate static struct devmap_ctx *devmap_ctxinit(dev_t, ulong_t);
233*0Sstevel@tonic-gate static struct devmap_softlock *devmap_softlock_init(dev_t, ulong_t);
234*0Sstevel@tonic-gate static void devmap_softlock_rele(devmap_handle_t *);
235*0Sstevel@tonic-gate static void devmap_ctx_rele(devmap_handle_t *);
236*0Sstevel@tonic-gate 
237*0Sstevel@tonic-gate static void devmap_ctxto(void *);
238*0Sstevel@tonic-gate 
239*0Sstevel@tonic-gate static devmap_handle_t *devmap_find_handle(devmap_handle_t *dhp_head,
240*0Sstevel@tonic-gate     caddr_t addr);
241*0Sstevel@tonic-gate 
242*0Sstevel@tonic-gate static ulong_t devmap_roundup(devmap_handle_t *dhp, ulong_t offset, size_t len,
243*0Sstevel@tonic-gate     ulong_t *opfn, ulong_t *pagesize);
244*0Sstevel@tonic-gate 
245*0Sstevel@tonic-gate static void free_devmap_handle(devmap_handle_t *dhp);
246*0Sstevel@tonic-gate 
247*0Sstevel@tonic-gate static int devmap_handle_dup(devmap_handle_t *dhp, devmap_handle_t **new_dhp,
248*0Sstevel@tonic-gate     struct seg *newseg);
249*0Sstevel@tonic-gate 
250*0Sstevel@tonic-gate static devmap_handle_t *devmap_handle_unmap(devmap_handle_t *dhp);
251*0Sstevel@tonic-gate 
252*0Sstevel@tonic-gate static void devmap_handle_unmap_head(devmap_handle_t *dhp, size_t len);
253*0Sstevel@tonic-gate 
254*0Sstevel@tonic-gate static void devmap_handle_unmap_tail(devmap_handle_t *dhp, caddr_t addr);
255*0Sstevel@tonic-gate 
256*0Sstevel@tonic-gate static int devmap_device(devmap_handle_t *dhp, struct as *as, caddr_t *addr,
257*0Sstevel@tonic-gate     offset_t off, size_t len, uint_t flags);
258*0Sstevel@tonic-gate 
259*0Sstevel@tonic-gate static void devmap_get_large_pgsize(devmap_handle_t *dhp, size_t len,
260*0Sstevel@tonic-gate     caddr_t addr, size_t *llen, caddr_t *laddr);
261*0Sstevel@tonic-gate 
262*0Sstevel@tonic-gate static void devmap_handle_reduce_len(devmap_handle_t *dhp, size_t len);
263*0Sstevel@tonic-gate 
264*0Sstevel@tonic-gate static void *devmap_alloc_pages(vmem_t *vmp, size_t size, int vmflag);
265*0Sstevel@tonic-gate static void devmap_free_pages(vmem_t *vmp, void *inaddr, size_t size);
266*0Sstevel@tonic-gate 
267*0Sstevel@tonic-gate static void *devmap_umem_alloc_np(size_t size, size_t flags);
268*0Sstevel@tonic-gate static void devmap_umem_free_np(void *addr, size_t size);
269*0Sstevel@tonic-gate 
270*0Sstevel@tonic-gate /*
271*0Sstevel@tonic-gate  * routines to lock and unlock underlying segkp segment for
272*0Sstevel@tonic-gate  * KMEM_PAGEABLE type cookies.
273*0Sstevel@tonic-gate  */
274*0Sstevel@tonic-gate static faultcode_t  acquire_kpmem_lock(struct ddi_umem_cookie *, size_t);
275*0Sstevel@tonic-gate static void release_kpmem_lock(struct ddi_umem_cookie *, size_t);
276*0Sstevel@tonic-gate 
277*0Sstevel@tonic-gate /*
278*0Sstevel@tonic-gate  * Routines to synchronize F_SOFTLOCK and F_INVAL faults for
279*0Sstevel@tonic-gate  * drivers with devmap_access callbacks
280*0Sstevel@tonic-gate  */
281*0Sstevel@tonic-gate static int devmap_softlock_enter(struct devmap_softlock *, size_t,
282*0Sstevel@tonic-gate 	enum fault_type);
283*0Sstevel@tonic-gate static void devmap_softlock_exit(struct devmap_softlock *, size_t,
284*0Sstevel@tonic-gate 	enum fault_type);
285*0Sstevel@tonic-gate 
286*0Sstevel@tonic-gate static kmutex_t devmapctx_lock;
287*0Sstevel@tonic-gate 
288*0Sstevel@tonic-gate static kmutex_t devmap_slock;
289*0Sstevel@tonic-gate 
290*0Sstevel@tonic-gate /*
291*0Sstevel@tonic-gate  * Initialize the thread callbacks and thread private data.
292*0Sstevel@tonic-gate  */
293*0Sstevel@tonic-gate static struct devmap_ctx *
294*0Sstevel@tonic-gate devmap_ctxinit(dev_t dev, ulong_t id)
295*0Sstevel@tonic-gate {
296*0Sstevel@tonic-gate 	struct devmap_ctx	*devctx;
297*0Sstevel@tonic-gate 	struct devmap_ctx	*tmp;
298*0Sstevel@tonic-gate 	dev_info_t		*dip;
299*0Sstevel@tonic-gate 
300*0Sstevel@tonic-gate 	tmp =  kmem_zalloc(sizeof (struct devmap_ctx), KM_SLEEP);
301*0Sstevel@tonic-gate 
302*0Sstevel@tonic-gate 	mutex_enter(&devmapctx_lock);
303*0Sstevel@tonic-gate 
304*0Sstevel@tonic-gate 	dip = e_ddi_hold_devi_by_dev(dev, 0);
305*0Sstevel@tonic-gate 	ASSERT(dip != NULL);
306*0Sstevel@tonic-gate 	ddi_release_devi(dip);
307*0Sstevel@tonic-gate 
308*0Sstevel@tonic-gate 	for (devctx = devmapctx_list; devctx != NULL; devctx = devctx->next)
309*0Sstevel@tonic-gate 		if ((devctx->dip == dip) && (devctx->id == id))
310*0Sstevel@tonic-gate 			break;
311*0Sstevel@tonic-gate 
312*0Sstevel@tonic-gate 	if (devctx == NULL) {
313*0Sstevel@tonic-gate 		devctx = tmp;
314*0Sstevel@tonic-gate 		devctx->dip = dip;
315*0Sstevel@tonic-gate 		devctx->id = id;
316*0Sstevel@tonic-gate 		mutex_init(&devctx->lock, NULL, MUTEX_DEFAULT, NULL);
317*0Sstevel@tonic-gate 		cv_init(&devctx->cv, NULL, CV_DEFAULT, NULL);
318*0Sstevel@tonic-gate 		devctx->next = devmapctx_list;
319*0Sstevel@tonic-gate 		devmapctx_list = devctx;
320*0Sstevel@tonic-gate 	} else
321*0Sstevel@tonic-gate 		kmem_free(tmp, sizeof (struct devmap_ctx));
322*0Sstevel@tonic-gate 
323*0Sstevel@tonic-gate 	mutex_enter(&devctx->lock);
324*0Sstevel@tonic-gate 	devctx->refcnt++;
325*0Sstevel@tonic-gate 	mutex_exit(&devctx->lock);
326*0Sstevel@tonic-gate 	mutex_exit(&devmapctx_lock);
327*0Sstevel@tonic-gate 
328*0Sstevel@tonic-gate 	return (devctx);
329*0Sstevel@tonic-gate }
330*0Sstevel@tonic-gate 
331*0Sstevel@tonic-gate /*
332*0Sstevel@tonic-gate  * Timeout callback called if a CPU has not given up the device context
333*0Sstevel@tonic-gate  * within dhp->dh_timeout_length ticks
334*0Sstevel@tonic-gate  */
335*0Sstevel@tonic-gate static void
336*0Sstevel@tonic-gate devmap_ctxto(void *data)
337*0Sstevel@tonic-gate {
338*0Sstevel@tonic-gate 	struct devmap_ctx *devctx = data;
339*0Sstevel@tonic-gate 
340*0Sstevel@tonic-gate 	TRACE_1(TR_FAC_DEVMAP, TR_DEVMAP_CTXTO,
341*0Sstevel@tonic-gate 	    "devmap_ctxto:timeout expired, devctx=%p", (void *)devctx);
342*0Sstevel@tonic-gate 	mutex_enter(&devctx->lock);
343*0Sstevel@tonic-gate 	/*
344*0Sstevel@tonic-gate 	 * Set oncpu = 0 so the next mapping trying to get the device context
345*0Sstevel@tonic-gate 	 * can.
346*0Sstevel@tonic-gate 	 */
347*0Sstevel@tonic-gate 	devctx->oncpu = 0;
348*0Sstevel@tonic-gate 	devctx->timeout = 0;
349*0Sstevel@tonic-gate 	cv_signal(&devctx->cv);
350*0Sstevel@tonic-gate 	mutex_exit(&devctx->lock);
351*0Sstevel@tonic-gate }
352*0Sstevel@tonic-gate 
353*0Sstevel@tonic-gate /*
354*0Sstevel@tonic-gate  * Create a device segment.
355*0Sstevel@tonic-gate  */
356*0Sstevel@tonic-gate int
357*0Sstevel@tonic-gate segdev_create(struct seg *seg, void *argsp)
358*0Sstevel@tonic-gate {
359*0Sstevel@tonic-gate 	struct segdev_data *sdp;
360*0Sstevel@tonic-gate 	struct segdev_crargs *a = (struct segdev_crargs *)argsp;
361*0Sstevel@tonic-gate 	devmap_handle_t *dhp = (devmap_handle_t *)a->devmap_data;
362*0Sstevel@tonic-gate 	int error;
363*0Sstevel@tonic-gate 
364*0Sstevel@tonic-gate 	/*
365*0Sstevel@tonic-gate 	 * Since the address space is "write" locked, we
366*0Sstevel@tonic-gate 	 * don't need the segment lock to protect "segdev" data.
367*0Sstevel@tonic-gate 	 */
368*0Sstevel@tonic-gate 	ASSERT(seg->s_as && AS_WRITE_HELD(seg->s_as, &seg->s_as->a_lock));
369*0Sstevel@tonic-gate 
370*0Sstevel@tonic-gate 	hat_map(seg->s_as->a_hat, seg->s_base, seg->s_size, HAT_MAP);
371*0Sstevel@tonic-gate 
372*0Sstevel@tonic-gate 	sdp = sdp_alloc();
373*0Sstevel@tonic-gate 
374*0Sstevel@tonic-gate 	sdp->mapfunc = a->mapfunc;
375*0Sstevel@tonic-gate 	sdp->offset = a->offset;
376*0Sstevel@tonic-gate 	sdp->prot = a->prot;
377*0Sstevel@tonic-gate 	sdp->maxprot = a->maxprot;
378*0Sstevel@tonic-gate 	sdp->type = a->type;
379*0Sstevel@tonic-gate 	sdp->pageprot = 0;
380*0Sstevel@tonic-gate 	sdp->softlockcnt = 0;
381*0Sstevel@tonic-gate 	sdp->vpage = NULL;
382*0Sstevel@tonic-gate 
383*0Sstevel@tonic-gate 	if (sdp->mapfunc == NULL)
384*0Sstevel@tonic-gate 		sdp->devmap_data = dhp;
385*0Sstevel@tonic-gate 	else
386*0Sstevel@tonic-gate 		sdp->devmap_data = dhp = NULL;
387*0Sstevel@tonic-gate 
388*0Sstevel@tonic-gate 	sdp->hat_flags = a->hat_flags;
389*0Sstevel@tonic-gate 	sdp->hat_attr = a->hat_attr;
390*0Sstevel@tonic-gate 
391*0Sstevel@tonic-gate 	/*
392*0Sstevel@tonic-gate 	 * Currently, hat_flags supports only HAT_LOAD_NOCONSIST
393*0Sstevel@tonic-gate 	 */
394*0Sstevel@tonic-gate 	ASSERT(!(sdp->hat_flags & ~HAT_LOAD_NOCONSIST));
395*0Sstevel@tonic-gate 
396*0Sstevel@tonic-gate 	/*
397*0Sstevel@tonic-gate 	 * Hold shadow vnode -- segdev only deals with
398*0Sstevel@tonic-gate 	 * character (VCHR) devices. We use the common
399*0Sstevel@tonic-gate 	 * vp to hang pages on.
400*0Sstevel@tonic-gate 	 */
401*0Sstevel@tonic-gate 	sdp->vp = specfind(a->dev, VCHR);
402*0Sstevel@tonic-gate 	ASSERT(sdp->vp != NULL);
403*0Sstevel@tonic-gate 
404*0Sstevel@tonic-gate 	seg->s_ops = &segdev_ops;
405*0Sstevel@tonic-gate 	seg->s_data = sdp;
406*0Sstevel@tonic-gate 
407*0Sstevel@tonic-gate 	while (dhp != NULL) {
408*0Sstevel@tonic-gate 		dhp->dh_seg = seg;
409*0Sstevel@tonic-gate 		dhp = dhp->dh_next;
410*0Sstevel@tonic-gate 	}
411*0Sstevel@tonic-gate 
412*0Sstevel@tonic-gate 	/*
413*0Sstevel@tonic-gate 	 * Inform the vnode of the new mapping.
414*0Sstevel@tonic-gate 	 */
415*0Sstevel@tonic-gate 	/*
416*0Sstevel@tonic-gate 	 * It is ok to use pass sdp->maxprot to ADDMAP rather than to use
417*0Sstevel@tonic-gate 	 * dhp specific maxprot because spec_addmap does not use maxprot.
418*0Sstevel@tonic-gate 	 */
419*0Sstevel@tonic-gate 	error = VOP_ADDMAP(VTOCVP(sdp->vp), sdp->offset,
420*0Sstevel@tonic-gate 	    seg->s_as, seg->s_base, seg->s_size,
421*0Sstevel@tonic-gate 	    sdp->prot, sdp->maxprot, sdp->type, CRED());
422*0Sstevel@tonic-gate 
423*0Sstevel@tonic-gate 	if (error != 0) {
424*0Sstevel@tonic-gate 		sdp->devmap_data = NULL;
425*0Sstevel@tonic-gate 		hat_unload(seg->s_as->a_hat, seg->s_base, seg->s_size,
426*0Sstevel@tonic-gate 		    HAT_UNLOAD_UNMAP);
427*0Sstevel@tonic-gate 	}
428*0Sstevel@tonic-gate 
429*0Sstevel@tonic-gate 	return (error);
430*0Sstevel@tonic-gate }
431*0Sstevel@tonic-gate 
432*0Sstevel@tonic-gate static struct segdev_data *
433*0Sstevel@tonic-gate sdp_alloc(void)
434*0Sstevel@tonic-gate {
435*0Sstevel@tonic-gate 	struct segdev_data *sdp;
436*0Sstevel@tonic-gate 
437*0Sstevel@tonic-gate 	sdp = kmem_zalloc(sizeof (struct segdev_data), KM_SLEEP);
438*0Sstevel@tonic-gate 	mutex_init(&sdp->lock, NULL, MUTEX_DEFAULT, NULL);
439*0Sstevel@tonic-gate 
440*0Sstevel@tonic-gate 	return (sdp);
441*0Sstevel@tonic-gate }
442*0Sstevel@tonic-gate 
443*0Sstevel@tonic-gate /*
444*0Sstevel@tonic-gate  * Duplicate seg and return new segment in newseg.
445*0Sstevel@tonic-gate  */
446*0Sstevel@tonic-gate static int
447*0Sstevel@tonic-gate segdev_dup(struct seg *seg, struct seg *newseg)
448*0Sstevel@tonic-gate {
449*0Sstevel@tonic-gate 	struct segdev_data *sdp = (struct segdev_data *)seg->s_data;
450*0Sstevel@tonic-gate 	struct segdev_data *newsdp;
451*0Sstevel@tonic-gate 	devmap_handle_t *dhp = (devmap_handle_t *)sdp->devmap_data;
452*0Sstevel@tonic-gate 	size_t npages;
453*0Sstevel@tonic-gate 	int ret;
454*0Sstevel@tonic-gate 
455*0Sstevel@tonic-gate 	TRACE_2(TR_FAC_DEVMAP, TR_DEVMAP_DUP,
456*0Sstevel@tonic-gate 	    "segdev_dup:start dhp=%p, seg=%p", (void *)dhp, (void *)seg);
457*0Sstevel@tonic-gate 
458*0Sstevel@tonic-gate 	DEBUGF(3, (CE_CONT, "segdev_dup: dhp %p seg %p\n",
459*0Sstevel@tonic-gate 	    (void *)dhp, (void *)seg));
460*0Sstevel@tonic-gate 
461*0Sstevel@tonic-gate 	/*
462*0Sstevel@tonic-gate 	 * Since the address space is "write" locked, we
463*0Sstevel@tonic-gate 	 * don't need the segment lock to protect "segdev" data.
464*0Sstevel@tonic-gate 	 */
465*0Sstevel@tonic-gate 	ASSERT(seg->s_as && AS_WRITE_HELD(seg->s_as, &seg->s_as->a_lock));
466*0Sstevel@tonic-gate 
467*0Sstevel@tonic-gate 	newsdp = sdp_alloc();
468*0Sstevel@tonic-gate 
469*0Sstevel@tonic-gate 	newseg->s_ops = seg->s_ops;
470*0Sstevel@tonic-gate 	newseg->s_data = (void *)newsdp;
471*0Sstevel@tonic-gate 
472*0Sstevel@tonic-gate 	VN_HOLD(sdp->vp);
473*0Sstevel@tonic-gate 	newsdp->vp 	= sdp->vp;
474*0Sstevel@tonic-gate 	newsdp->mapfunc = sdp->mapfunc;
475*0Sstevel@tonic-gate 	newsdp->offset	= sdp->offset;
476*0Sstevel@tonic-gate 	newsdp->pageprot = sdp->pageprot;
477*0Sstevel@tonic-gate 	newsdp->prot	= sdp->prot;
478*0Sstevel@tonic-gate 	newsdp->maxprot = sdp->maxprot;
479*0Sstevel@tonic-gate 	newsdp->type = sdp->type;
480*0Sstevel@tonic-gate 	newsdp->hat_attr = sdp->hat_attr;
481*0Sstevel@tonic-gate 	newsdp->hat_flags = sdp->hat_flags;
482*0Sstevel@tonic-gate 	newsdp->softlockcnt = 0;
483*0Sstevel@tonic-gate 
484*0Sstevel@tonic-gate 	/*
485*0Sstevel@tonic-gate 	 * Initialize per page data if the segment we are
486*0Sstevel@tonic-gate 	 * dup'ing has per page information.
487*0Sstevel@tonic-gate 	 */
488*0Sstevel@tonic-gate 	npages = seg_pages(newseg);
489*0Sstevel@tonic-gate 
490*0Sstevel@tonic-gate 	if (sdp->vpage != NULL) {
491*0Sstevel@tonic-gate 		size_t nbytes = vpgtob(npages);
492*0Sstevel@tonic-gate 
493*0Sstevel@tonic-gate 		newsdp->vpage = kmem_zalloc(nbytes, KM_SLEEP);
494*0Sstevel@tonic-gate 		bcopy(sdp->vpage, newsdp->vpage, nbytes);
495*0Sstevel@tonic-gate 	} else
496*0Sstevel@tonic-gate 		newsdp->vpage = NULL;
497*0Sstevel@tonic-gate 
498*0Sstevel@tonic-gate 	/*
499*0Sstevel@tonic-gate 	 * duplicate devmap handles
500*0Sstevel@tonic-gate 	 */
501*0Sstevel@tonic-gate 	if (dhp != NULL) {
502*0Sstevel@tonic-gate 		ret = devmap_handle_dup(dhp,
503*0Sstevel@tonic-gate 			(devmap_handle_t **)&newsdp->devmap_data, newseg);
504*0Sstevel@tonic-gate 		if (ret != 0) {
505*0Sstevel@tonic-gate 			TRACE_3(TR_FAC_DEVMAP, TR_DEVMAP_DUP_CK1,
506*0Sstevel@tonic-gate 			    "segdev_dup:ret1 ret=%x, dhp=%p seg=%p",
507*0Sstevel@tonic-gate 			    ret, (void *)dhp, (void *)seg);
508*0Sstevel@tonic-gate 			DEBUGF(1, (CE_CONT,
509*0Sstevel@tonic-gate 			    "segdev_dup: ret %x dhp %p seg %p\n",
510*0Sstevel@tonic-gate 			    ret, (void *)dhp, (void *)seg));
511*0Sstevel@tonic-gate 			return (ret);
512*0Sstevel@tonic-gate 		}
513*0Sstevel@tonic-gate 	}
514*0Sstevel@tonic-gate 
515*0Sstevel@tonic-gate 	/*
516*0Sstevel@tonic-gate 	 * Inform the common vnode of the new mapping.
517*0Sstevel@tonic-gate 	 */
518*0Sstevel@tonic-gate 	return (VOP_ADDMAP(VTOCVP(newsdp->vp),
519*0Sstevel@tonic-gate 		newsdp->offset, newseg->s_as,
520*0Sstevel@tonic-gate 		newseg->s_base, newseg->s_size, newsdp->prot,
521*0Sstevel@tonic-gate 		newsdp->maxprot, sdp->type, CRED()));
522*0Sstevel@tonic-gate }
523*0Sstevel@tonic-gate 
524*0Sstevel@tonic-gate /*
525*0Sstevel@tonic-gate  * duplicate devmap handles
526*0Sstevel@tonic-gate  */
527*0Sstevel@tonic-gate static int
528*0Sstevel@tonic-gate devmap_handle_dup(devmap_handle_t *dhp, devmap_handle_t **new_dhp,
529*0Sstevel@tonic-gate     struct seg *newseg)
530*0Sstevel@tonic-gate {
531*0Sstevel@tonic-gate 	devmap_handle_t *newdhp_save = NULL;
532*0Sstevel@tonic-gate 	devmap_handle_t *newdhp = NULL;
533*0Sstevel@tonic-gate 	struct devmap_callback_ctl *callbackops;
534*0Sstevel@tonic-gate 
535*0Sstevel@tonic-gate 	while (dhp != NULL) {
536*0Sstevel@tonic-gate 		newdhp = kmem_alloc(sizeof (devmap_handle_t), KM_SLEEP);
537*0Sstevel@tonic-gate 
538*0Sstevel@tonic-gate 		/* Need to lock the original dhp while copying if REMAP */
539*0Sstevel@tonic-gate 		HOLD_DHP_LOCK(dhp);
540*0Sstevel@tonic-gate 		bcopy(dhp, newdhp, sizeof (devmap_handle_t));
541*0Sstevel@tonic-gate 		RELE_DHP_LOCK(dhp);
542*0Sstevel@tonic-gate 		newdhp->dh_seg = newseg;
543*0Sstevel@tonic-gate 		newdhp->dh_next = NULL;
544*0Sstevel@tonic-gate 		if (newdhp_save != NULL)
545*0Sstevel@tonic-gate 			newdhp_save->dh_next = newdhp;
546*0Sstevel@tonic-gate 		else
547*0Sstevel@tonic-gate 			*new_dhp = newdhp;
548*0Sstevel@tonic-gate 		newdhp_save = newdhp;
549*0Sstevel@tonic-gate 
550*0Sstevel@tonic-gate 		callbackops = &newdhp->dh_callbackops;
551*0Sstevel@tonic-gate 
552*0Sstevel@tonic-gate 		if (dhp->dh_softlock != NULL)
553*0Sstevel@tonic-gate 			newdhp->dh_softlock = devmap_softlock_init(
554*0Sstevel@tonic-gate 			    newdhp->dh_dev,
555*0Sstevel@tonic-gate 			    (ulong_t)callbackops->devmap_access);
556*0Sstevel@tonic-gate 		if (dhp->dh_ctx != NULL)
557*0Sstevel@tonic-gate 			newdhp->dh_ctx = devmap_ctxinit(newdhp->dh_dev,
558*0Sstevel@tonic-gate 			    (ulong_t)callbackops->devmap_access);
559*0Sstevel@tonic-gate 
560*0Sstevel@tonic-gate 		/*
561*0Sstevel@tonic-gate 		 * Initialize dh_lock if we want to do remap.
562*0Sstevel@tonic-gate 		 */
563*0Sstevel@tonic-gate 		if (newdhp->dh_flags & DEVMAP_ALLOW_REMAP) {
564*0Sstevel@tonic-gate 			mutex_init(&newdhp->dh_lock, NULL, MUTEX_DEFAULT, NULL);
565*0Sstevel@tonic-gate 			newdhp->dh_flags |= DEVMAP_LOCK_INITED;
566*0Sstevel@tonic-gate 		}
567*0Sstevel@tonic-gate 
568*0Sstevel@tonic-gate 		if (callbackops->devmap_dup != NULL) {
569*0Sstevel@tonic-gate 			int ret;
570*0Sstevel@tonic-gate 
571*0Sstevel@tonic-gate 			/*
572*0Sstevel@tonic-gate 			 * Call the dup callback so that the driver can
573*0Sstevel@tonic-gate 			 * duplicate its private data.
574*0Sstevel@tonic-gate 			 */
575*0Sstevel@tonic-gate 			ret = (*callbackops->devmap_dup)(dhp, dhp->dh_pvtp,
576*0Sstevel@tonic-gate 				(devmap_cookie_t *)newdhp, &newdhp->dh_pvtp);
577*0Sstevel@tonic-gate 
578*0Sstevel@tonic-gate 			if (ret != 0) {
579*0Sstevel@tonic-gate 				/*
580*0Sstevel@tonic-gate 				 * We want to free up this segment as the driver
581*0Sstevel@tonic-gate 				 * has indicated that we can't dup it.  But we
582*0Sstevel@tonic-gate 				 * don't want to call the drivers, devmap_unmap,
583*0Sstevel@tonic-gate 				 * callback function as the driver does not
584*0Sstevel@tonic-gate 				 * think this segment exists. The caller of
585*0Sstevel@tonic-gate 				 * devmap_dup will call seg_free on newseg
586*0Sstevel@tonic-gate 				 * as it was the caller that allocated the
587*0Sstevel@tonic-gate 				 * segment.
588*0Sstevel@tonic-gate 				 */
589*0Sstevel@tonic-gate 				DEBUGF(1, (CE_CONT, "devmap_handle_dup ERROR: "
590*0Sstevel@tonic-gate 				    "newdhp %p dhp %p\n", (void *)newdhp,
591*0Sstevel@tonic-gate 				    (void *)dhp));
592*0Sstevel@tonic-gate 				callbackops->devmap_unmap = NULL;
593*0Sstevel@tonic-gate 				return (ret);
594*0Sstevel@tonic-gate 			}
595*0Sstevel@tonic-gate 		}
596*0Sstevel@tonic-gate 
597*0Sstevel@tonic-gate 		dhp = dhp->dh_next;
598*0Sstevel@tonic-gate 	}
599*0Sstevel@tonic-gate 
600*0Sstevel@tonic-gate 	return (0);
601*0Sstevel@tonic-gate }
602*0Sstevel@tonic-gate 
603*0Sstevel@tonic-gate /*
604*0Sstevel@tonic-gate  * Split a segment at addr for length len.
605*0Sstevel@tonic-gate  */
606*0Sstevel@tonic-gate /*ARGSUSED*/
607*0Sstevel@tonic-gate static int
608*0Sstevel@tonic-gate segdev_unmap(struct seg *seg, caddr_t addr, size_t len)
609*0Sstevel@tonic-gate {
610*0Sstevel@tonic-gate 	register struct segdev_data *sdp = (struct segdev_data *)seg->s_data;
611*0Sstevel@tonic-gate 	register struct segdev_data *nsdp;
612*0Sstevel@tonic-gate 	register struct seg *nseg;
613*0Sstevel@tonic-gate 	register size_t	opages;		/* old segment size in pages */
614*0Sstevel@tonic-gate 	register size_t	npages;		/* new segment size in pages */
615*0Sstevel@tonic-gate 	register size_t	dpages;		/* pages being deleted (unmapped) */
616*0Sstevel@tonic-gate 	register size_t	nbytes;
617*0Sstevel@tonic-gate 	devmap_handle_t *dhp = (devmap_handle_t *)sdp->devmap_data;
618*0Sstevel@tonic-gate 	devmap_handle_t *dhpp;
619*0Sstevel@tonic-gate 	devmap_handle_t *newdhp;
620*0Sstevel@tonic-gate 	struct devmap_callback_ctl *callbackops;
621*0Sstevel@tonic-gate 	caddr_t nbase;
622*0Sstevel@tonic-gate 	offset_t off;
623*0Sstevel@tonic-gate 	ulong_t nsize;
624*0Sstevel@tonic-gate 	size_t mlen, sz;
625*0Sstevel@tonic-gate 
626*0Sstevel@tonic-gate 	TRACE_4(TR_FAC_DEVMAP, TR_DEVMAP_UNMAP,
627*0Sstevel@tonic-gate 	    "segdev_unmap:start dhp=%p, seg=%p addr=%p len=%lx",
628*0Sstevel@tonic-gate 	    (void *)dhp, (void *)seg, (void *)addr, len);
629*0Sstevel@tonic-gate 
630*0Sstevel@tonic-gate 	DEBUGF(3, (CE_CONT, "segdev_unmap: dhp %p seg %p addr %p len %lx\n",
631*0Sstevel@tonic-gate 	    (void *)dhp, (void *)seg, (void *)addr, len));
632*0Sstevel@tonic-gate 
633*0Sstevel@tonic-gate 	/*
634*0Sstevel@tonic-gate 	 * Since the address space is "write" locked, we
635*0Sstevel@tonic-gate 	 * don't need the segment lock to protect "segdev" data.
636*0Sstevel@tonic-gate 	 */
637*0Sstevel@tonic-gate 	ASSERT(seg->s_as && AS_WRITE_HELD(seg->s_as, &seg->s_as->a_lock));
638*0Sstevel@tonic-gate 
639*0Sstevel@tonic-gate 	if ((sz = sdp->softlockcnt) > 0) {
640*0Sstevel@tonic-gate 		/*
641*0Sstevel@tonic-gate 		 * Fail the unmap if pages are SOFTLOCKed through this mapping.
642*0Sstevel@tonic-gate 		 * softlockcnt is protected from change by the as write lock.
643*0Sstevel@tonic-gate 		 */
644*0Sstevel@tonic-gate 		TRACE_1(TR_FAC_DEVMAP, TR_DEVMAP_UNMAP_CK1,
645*0Sstevel@tonic-gate 		    "segdev_unmap:error softlockcnt = %ld", sz);
646*0Sstevel@tonic-gate 		DEBUGF(1, (CE_CONT, "segdev_unmap: softlockcnt %ld\n", sz));
647*0Sstevel@tonic-gate 		return (EAGAIN);
648*0Sstevel@tonic-gate 	}
649*0Sstevel@tonic-gate 
650*0Sstevel@tonic-gate 	/*
651*0Sstevel@tonic-gate 	 * Check for bad sizes
652*0Sstevel@tonic-gate 	 */
653*0Sstevel@tonic-gate 	if (addr < seg->s_base || addr + len > seg->s_base + seg->s_size ||
654*0Sstevel@tonic-gate 	    (len & PAGEOFFSET) || ((uintptr_t)addr & PAGEOFFSET))
655*0Sstevel@tonic-gate 		panic("segdev_unmap");
656*0Sstevel@tonic-gate 
657*0Sstevel@tonic-gate 	if (dhp != NULL) {
658*0Sstevel@tonic-gate 		devmap_handle_t *tdhp;
659*0Sstevel@tonic-gate 		/*
660*0Sstevel@tonic-gate 		 * If large page size was used in hat_devload(),
661*0Sstevel@tonic-gate 		 * the same page size must be used in hat_unload().
662*0Sstevel@tonic-gate 		 */
663*0Sstevel@tonic-gate 		dhpp = tdhp = devmap_find_handle(dhp, addr);
664*0Sstevel@tonic-gate 		while (tdhp != NULL) {
665*0Sstevel@tonic-gate 			if (tdhp->dh_flags & DEVMAP_FLAG_LARGE) {
666*0Sstevel@tonic-gate 				break;
667*0Sstevel@tonic-gate 			}
668*0Sstevel@tonic-gate 			tdhp = tdhp->dh_next;
669*0Sstevel@tonic-gate 		}
670*0Sstevel@tonic-gate 		if (tdhp != NULL) {	/* found a dhp using large pages */
671*0Sstevel@tonic-gate 			size_t slen = len;
672*0Sstevel@tonic-gate 			size_t mlen;
673*0Sstevel@tonic-gate 			size_t soff;
674*0Sstevel@tonic-gate 
675*0Sstevel@tonic-gate 			soff = (ulong_t)(addr - dhpp->dh_uvaddr);
676*0Sstevel@tonic-gate 			while (slen != 0) {
677*0Sstevel@tonic-gate 				mlen = MIN(slen, (dhpp->dh_len - soff));
678*0Sstevel@tonic-gate 				hat_unload(seg->s_as->a_hat, dhpp->dh_uvaddr,
679*0Sstevel@tonic-gate 					dhpp->dh_len, HAT_UNLOAD_UNMAP);
680*0Sstevel@tonic-gate 				dhpp = dhpp->dh_next;
681*0Sstevel@tonic-gate 				ASSERT(slen >= mlen);
682*0Sstevel@tonic-gate 				slen -= mlen;
683*0Sstevel@tonic-gate 				soff = 0;
684*0Sstevel@tonic-gate 			}
685*0Sstevel@tonic-gate 		} else
686*0Sstevel@tonic-gate 			hat_unload(seg->s_as->a_hat, addr, len,
687*0Sstevel@tonic-gate 				HAT_UNLOAD_UNMAP);
688*0Sstevel@tonic-gate 	} else {
689*0Sstevel@tonic-gate 		/*
690*0Sstevel@tonic-gate 		 * Unload any hardware translations in the range
691*0Sstevel@tonic-gate 		 * to be taken out.
692*0Sstevel@tonic-gate 		 */
693*0Sstevel@tonic-gate 		hat_unload(seg->s_as->a_hat, addr, len, HAT_UNLOAD_UNMAP);
694*0Sstevel@tonic-gate 	}
695*0Sstevel@tonic-gate 
696*0Sstevel@tonic-gate 	/*
697*0Sstevel@tonic-gate 	 * get the user offset which will used in the driver callbacks
698*0Sstevel@tonic-gate 	 */
699*0Sstevel@tonic-gate 	off = sdp->offset + (offset_t)(addr - seg->s_base);
700*0Sstevel@tonic-gate 
701*0Sstevel@tonic-gate 	/*
702*0Sstevel@tonic-gate 	 * Inform the vnode of the unmapping.
703*0Sstevel@tonic-gate 	 */
704*0Sstevel@tonic-gate 	ASSERT(sdp->vp != NULL);
705*0Sstevel@tonic-gate 	(void) VOP_DELMAP(VTOCVP(sdp->vp), off, seg->s_as, addr, len,
706*0Sstevel@tonic-gate 		sdp->prot, sdp->maxprot, sdp->type, CRED());
707*0Sstevel@tonic-gate 
708*0Sstevel@tonic-gate 	/*
709*0Sstevel@tonic-gate 	 * Check for entire segment
710*0Sstevel@tonic-gate 	 */
711*0Sstevel@tonic-gate 	if (addr == seg->s_base && len == seg->s_size) {
712*0Sstevel@tonic-gate 		seg_free(seg);
713*0Sstevel@tonic-gate 		return (0);
714*0Sstevel@tonic-gate 	}
715*0Sstevel@tonic-gate 
716*0Sstevel@tonic-gate 	opages = seg_pages(seg);
717*0Sstevel@tonic-gate 	dpages = btop(len);
718*0Sstevel@tonic-gate 	npages = opages - dpages;
719*0Sstevel@tonic-gate 
720*0Sstevel@tonic-gate 	/*
721*0Sstevel@tonic-gate 	 * Check for beginning of segment
722*0Sstevel@tonic-gate 	 */
723*0Sstevel@tonic-gate 	if (addr == seg->s_base) {
724*0Sstevel@tonic-gate 		if (sdp->vpage != NULL) {
725*0Sstevel@tonic-gate 			register struct vpage *ovpage;
726*0Sstevel@tonic-gate 
727*0Sstevel@tonic-gate 			ovpage = sdp->vpage;	/* keep pointer to vpage */
728*0Sstevel@tonic-gate 
729*0Sstevel@tonic-gate 			nbytes = vpgtob(npages);
730*0Sstevel@tonic-gate 			sdp->vpage = kmem_alloc(nbytes, KM_SLEEP);
731*0Sstevel@tonic-gate 			bcopy(&ovpage[dpages], sdp->vpage, nbytes);
732*0Sstevel@tonic-gate 
733*0Sstevel@tonic-gate 			/* free up old vpage */
734*0Sstevel@tonic-gate 			kmem_free(ovpage, vpgtob(opages));
735*0Sstevel@tonic-gate 		}
736*0Sstevel@tonic-gate 
737*0Sstevel@tonic-gate 		/*
738*0Sstevel@tonic-gate 		 * free devmap handles from the beginning of the mapping.
739*0Sstevel@tonic-gate 		 */
740*0Sstevel@tonic-gate 		if (dhp != NULL)
741*0Sstevel@tonic-gate 			devmap_handle_unmap_head(dhp, len);
742*0Sstevel@tonic-gate 
743*0Sstevel@tonic-gate 		sdp->offset += (offset_t)len;
744*0Sstevel@tonic-gate 
745*0Sstevel@tonic-gate 		seg->s_base += len;
746*0Sstevel@tonic-gate 		seg->s_size -= len;
747*0Sstevel@tonic-gate 
748*0Sstevel@tonic-gate 		return (0);
749*0Sstevel@tonic-gate 	}
750*0Sstevel@tonic-gate 
751*0Sstevel@tonic-gate 	/*
752*0Sstevel@tonic-gate 	 * Check for end of segment
753*0Sstevel@tonic-gate 	 */
754*0Sstevel@tonic-gate 	if (addr + len == seg->s_base + seg->s_size) {
755*0Sstevel@tonic-gate 		if (sdp->vpage != NULL) {
756*0Sstevel@tonic-gate 			register struct vpage *ovpage;
757*0Sstevel@tonic-gate 
758*0Sstevel@tonic-gate 			ovpage = sdp->vpage;	/* keep pointer to vpage */
759*0Sstevel@tonic-gate 
760*0Sstevel@tonic-gate 			nbytes = vpgtob(npages);
761*0Sstevel@tonic-gate 			sdp->vpage = kmem_alloc(nbytes, KM_SLEEP);
762*0Sstevel@tonic-gate 			bcopy(ovpage, sdp->vpage, nbytes);
763*0Sstevel@tonic-gate 
764*0Sstevel@tonic-gate 			/* free up old vpage */
765*0Sstevel@tonic-gate 			kmem_free(ovpage, vpgtob(opages));
766*0Sstevel@tonic-gate 		}
767*0Sstevel@tonic-gate 		seg->s_size -= len;
768*0Sstevel@tonic-gate 
769*0Sstevel@tonic-gate 		/*
770*0Sstevel@tonic-gate 		 * free devmap handles from addr to the end of the mapping.
771*0Sstevel@tonic-gate 		 */
772*0Sstevel@tonic-gate 		if (dhp != NULL)
773*0Sstevel@tonic-gate 			devmap_handle_unmap_tail(dhp, addr);
774*0Sstevel@tonic-gate 
775*0Sstevel@tonic-gate 		return (0);
776*0Sstevel@tonic-gate 	}
777*0Sstevel@tonic-gate 
778*0Sstevel@tonic-gate 	/*
779*0Sstevel@tonic-gate 	 * The section to go is in the middle of the segment,
780*0Sstevel@tonic-gate 	 * have to make it into two segments.  nseg is made for
781*0Sstevel@tonic-gate 	 * the high end while seg is cut down at the low end.
782*0Sstevel@tonic-gate 	 */
783*0Sstevel@tonic-gate 	nbase = addr + len;				/* new seg base */
784*0Sstevel@tonic-gate 	nsize = (seg->s_base + seg->s_size) - nbase;	/* new seg size */
785*0Sstevel@tonic-gate 	seg->s_size = addr - seg->s_base;		/* shrink old seg */
786*0Sstevel@tonic-gate 	nseg = seg_alloc(seg->s_as, nbase, nsize);
787*0Sstevel@tonic-gate 	if (nseg == NULL)
788*0Sstevel@tonic-gate 		panic("segdev_unmap seg_alloc");
789*0Sstevel@tonic-gate 
790*0Sstevel@tonic-gate 	TRACE_2(TR_FAC_DEVMAP, TR_DEVMAP_UNMAP_CK2,
791*0Sstevel@tonic-gate 	    "segdev_unmap: seg=%p nseg=%p", (void *)seg, (void *)nseg);
792*0Sstevel@tonic-gate 	DEBUGF(3, (CE_CONT, "segdev_unmap: segdev_dup seg %p nseg %p\n",
793*0Sstevel@tonic-gate 	    (void *)seg, (void *)nseg));
794*0Sstevel@tonic-gate 	nsdp = sdp_alloc();
795*0Sstevel@tonic-gate 
796*0Sstevel@tonic-gate 	nseg->s_ops = seg->s_ops;
797*0Sstevel@tonic-gate 	nseg->s_data = (void *)nsdp;
798*0Sstevel@tonic-gate 
799*0Sstevel@tonic-gate 	VN_HOLD(sdp->vp);
800*0Sstevel@tonic-gate 	nsdp->mapfunc = sdp->mapfunc;
801*0Sstevel@tonic-gate 	nsdp->offset = sdp->offset + (offset_t)(nseg->s_base - seg->s_base);
802*0Sstevel@tonic-gate 	nsdp->vp 	= sdp->vp;
803*0Sstevel@tonic-gate 	nsdp->pageprot = sdp->pageprot;
804*0Sstevel@tonic-gate 	nsdp->prot	= sdp->prot;
805*0Sstevel@tonic-gate 	nsdp->maxprot = sdp->maxprot;
806*0Sstevel@tonic-gate 	nsdp->type = sdp->type;
807*0Sstevel@tonic-gate 	nsdp->hat_attr = sdp->hat_attr;
808*0Sstevel@tonic-gate 	nsdp->hat_flags = sdp->hat_flags;
809*0Sstevel@tonic-gate 	nsdp->softlockcnt = 0;
810*0Sstevel@tonic-gate 
811*0Sstevel@tonic-gate 	/*
812*0Sstevel@tonic-gate 	 * Initialize per page data if the segment we are
813*0Sstevel@tonic-gate 	 * dup'ing has per page information.
814*0Sstevel@tonic-gate 	 */
815*0Sstevel@tonic-gate 	if (sdp->vpage != NULL) {
816*0Sstevel@tonic-gate 		/* need to split vpage into two arrays */
817*0Sstevel@tonic-gate 		register size_t nnbytes;
818*0Sstevel@tonic-gate 		register size_t nnpages;
819*0Sstevel@tonic-gate 		register struct vpage *ovpage;
820*0Sstevel@tonic-gate 
821*0Sstevel@tonic-gate 		ovpage = sdp->vpage;		/* keep pointer to vpage */
822*0Sstevel@tonic-gate 
823*0Sstevel@tonic-gate 		npages = seg_pages(seg);	/* seg has shrunk */
824*0Sstevel@tonic-gate 		nbytes = vpgtob(npages);
825*0Sstevel@tonic-gate 		nnpages = seg_pages(nseg);
826*0Sstevel@tonic-gate 		nnbytes = vpgtob(nnpages);
827*0Sstevel@tonic-gate 
828*0Sstevel@tonic-gate 		sdp->vpage = kmem_alloc(nbytes, KM_SLEEP);
829*0Sstevel@tonic-gate 		bcopy(ovpage, sdp->vpage, nbytes);
830*0Sstevel@tonic-gate 
831*0Sstevel@tonic-gate 		nsdp->vpage = kmem_alloc(nnbytes, KM_SLEEP);
832*0Sstevel@tonic-gate 		bcopy(&ovpage[npages + dpages], nsdp->vpage, nnbytes);
833*0Sstevel@tonic-gate 
834*0Sstevel@tonic-gate 		/* free up old vpage */
835*0Sstevel@tonic-gate 		kmem_free(ovpage, vpgtob(opages));
836*0Sstevel@tonic-gate 	} else
837*0Sstevel@tonic-gate 		nsdp->vpage = NULL;
838*0Sstevel@tonic-gate 
839*0Sstevel@tonic-gate 	/*
840*0Sstevel@tonic-gate 	 * unmap dhps.
841*0Sstevel@tonic-gate 	 */
842*0Sstevel@tonic-gate 	if (dhp == NULL) {
843*0Sstevel@tonic-gate 		nsdp->devmap_data = NULL;
844*0Sstevel@tonic-gate 		return (0);
845*0Sstevel@tonic-gate 	}
846*0Sstevel@tonic-gate 	while (dhp != NULL) {
847*0Sstevel@tonic-gate 		callbackops = &dhp->dh_callbackops;
848*0Sstevel@tonic-gate 		TRACE_2(TR_FAC_DEVMAP, TR_DEVMAP_UNMAP_CK3,
849*0Sstevel@tonic-gate 		    "segdev_unmap: dhp=%p addr=%p", dhp, addr);
850*0Sstevel@tonic-gate 		DEBUGF(3, (CE_CONT, "unmap: dhp %p addr %p uvaddr %p len %lx\n",
851*0Sstevel@tonic-gate 		    (void *)dhp, (void *)addr,
852*0Sstevel@tonic-gate 		    (void *)dhp->dh_uvaddr, dhp->dh_len));
853*0Sstevel@tonic-gate 
854*0Sstevel@tonic-gate 		if (addr == (dhp->dh_uvaddr + dhp->dh_len)) {
855*0Sstevel@tonic-gate 			dhpp = dhp->dh_next;
856*0Sstevel@tonic-gate 			dhp->dh_next = NULL;
857*0Sstevel@tonic-gate 			dhp = dhpp;
858*0Sstevel@tonic-gate 		} else if (addr > (dhp->dh_uvaddr + dhp->dh_len)) {
859*0Sstevel@tonic-gate 			dhp = dhp->dh_next;
860*0Sstevel@tonic-gate 		} else if (addr > dhp->dh_uvaddr &&
861*0Sstevel@tonic-gate 			(addr + len) < (dhp->dh_uvaddr + dhp->dh_len)) {
862*0Sstevel@tonic-gate 			/*
863*0Sstevel@tonic-gate 			 * <addr, addr+len> is enclosed by dhp.
864*0Sstevel@tonic-gate 			 * create a newdhp that begins at addr+len and
865*0Sstevel@tonic-gate 			 * ends at dhp->dh_uvaddr+dhp->dh_len.
866*0Sstevel@tonic-gate 			 */
867*0Sstevel@tonic-gate 			newdhp = kmem_alloc(sizeof (devmap_handle_t), KM_SLEEP);
868*0Sstevel@tonic-gate 			HOLD_DHP_LOCK(dhp);
869*0Sstevel@tonic-gate 			bcopy(dhp, newdhp, sizeof (devmap_handle_t));
870*0Sstevel@tonic-gate 			RELE_DHP_LOCK(dhp);
871*0Sstevel@tonic-gate 			newdhp->dh_seg = nseg;
872*0Sstevel@tonic-gate 			newdhp->dh_next = dhp->dh_next;
873*0Sstevel@tonic-gate 			if (dhp->dh_softlock != NULL)
874*0Sstevel@tonic-gate 				newdhp->dh_softlock = devmap_softlock_init(
875*0Sstevel@tonic-gate 					newdhp->dh_dev,
876*0Sstevel@tonic-gate 					(ulong_t)callbackops->devmap_access);
877*0Sstevel@tonic-gate 			if (dhp->dh_ctx != NULL)
878*0Sstevel@tonic-gate 				newdhp->dh_ctx = devmap_ctxinit(newdhp->dh_dev,
879*0Sstevel@tonic-gate 					(ulong_t)callbackops->devmap_access);
880*0Sstevel@tonic-gate 			if (newdhp->dh_flags & DEVMAP_LOCK_INITED) {
881*0Sstevel@tonic-gate 				mutex_init(&newdhp->dh_lock,
882*0Sstevel@tonic-gate 				    NULL, MUTEX_DEFAULT, NULL);
883*0Sstevel@tonic-gate 			}
884*0Sstevel@tonic-gate 			if (callbackops->devmap_unmap != NULL)
885*0Sstevel@tonic-gate 				(*callbackops->devmap_unmap)(dhp, dhp->dh_pvtp,
886*0Sstevel@tonic-gate 					off, len, dhp, &dhp->dh_pvtp,
887*0Sstevel@tonic-gate 					newdhp, &newdhp->dh_pvtp);
888*0Sstevel@tonic-gate 			mlen = len + (addr - dhp->dh_uvaddr);
889*0Sstevel@tonic-gate 			devmap_handle_reduce_len(newdhp, mlen);
890*0Sstevel@tonic-gate 			nsdp->devmap_data = newdhp;
891*0Sstevel@tonic-gate 			/* XX Changing len should recalculate LARGE flag */
892*0Sstevel@tonic-gate 			dhp->dh_len = addr - dhp->dh_uvaddr;
893*0Sstevel@tonic-gate 			dhpp = dhp->dh_next;
894*0Sstevel@tonic-gate 			dhp->dh_next = NULL;
895*0Sstevel@tonic-gate 			dhp = dhpp;
896*0Sstevel@tonic-gate 		} else if ((addr > dhp->dh_uvaddr) &&
897*0Sstevel@tonic-gate 			    ((addr + len) >= (dhp->dh_uvaddr + dhp->dh_len))) {
898*0Sstevel@tonic-gate 			mlen = dhp->dh_len + dhp->dh_uvaddr - addr;
899*0Sstevel@tonic-gate 			/*
900*0Sstevel@tonic-gate 			 * <addr, addr+len> spans over dhps.
901*0Sstevel@tonic-gate 			 */
902*0Sstevel@tonic-gate 			if (callbackops->devmap_unmap != NULL)
903*0Sstevel@tonic-gate 				(*callbackops->devmap_unmap)(dhp, dhp->dh_pvtp,
904*0Sstevel@tonic-gate 					off, mlen, (devmap_cookie_t *)dhp,
905*0Sstevel@tonic-gate 					&dhp->dh_pvtp, NULL, NULL);
906*0Sstevel@tonic-gate 			/* XX Changing len should recalculate LARGE flag */
907*0Sstevel@tonic-gate 			dhp->dh_len = addr - dhp->dh_uvaddr;
908*0Sstevel@tonic-gate 			dhpp = dhp->dh_next;
909*0Sstevel@tonic-gate 			dhp->dh_next = NULL;
910*0Sstevel@tonic-gate 			dhp = dhpp;
911*0Sstevel@tonic-gate 			nsdp->devmap_data = dhp;
912*0Sstevel@tonic-gate 		} else if ((addr + len) >= (dhp->dh_uvaddr + dhp->dh_len)) {
913*0Sstevel@tonic-gate 			/*
914*0Sstevel@tonic-gate 			 * dhp is enclosed by <addr, addr+len>.
915*0Sstevel@tonic-gate 			 */
916*0Sstevel@tonic-gate 			dhp->dh_seg = nseg;
917*0Sstevel@tonic-gate 			nsdp->devmap_data = dhp;
918*0Sstevel@tonic-gate 			dhp = devmap_handle_unmap(dhp);
919*0Sstevel@tonic-gate 			nsdp->devmap_data = dhp; /* XX redundant? */
920*0Sstevel@tonic-gate 		} else if (((addr + len) > dhp->dh_uvaddr) &&
921*0Sstevel@tonic-gate 			    ((addr + len) < (dhp->dh_uvaddr + dhp->dh_len))) {
922*0Sstevel@tonic-gate 			mlen = addr + len - dhp->dh_uvaddr;
923*0Sstevel@tonic-gate 			if (callbackops->devmap_unmap != NULL)
924*0Sstevel@tonic-gate 				(*callbackops->devmap_unmap)(dhp, dhp->dh_pvtp,
925*0Sstevel@tonic-gate 					dhp->dh_uoff, mlen, NULL,
926*0Sstevel@tonic-gate 					NULL, dhp, &dhp->dh_pvtp);
927*0Sstevel@tonic-gate 			devmap_handle_reduce_len(dhp, mlen);
928*0Sstevel@tonic-gate 			nsdp->devmap_data = dhp;
929*0Sstevel@tonic-gate 			dhp->dh_seg = nseg;
930*0Sstevel@tonic-gate 			dhp = dhp->dh_next;
931*0Sstevel@tonic-gate 		} else {
932*0Sstevel@tonic-gate 			dhp->dh_seg = nseg;
933*0Sstevel@tonic-gate 			dhp = dhp->dh_next;
934*0Sstevel@tonic-gate 		}
935*0Sstevel@tonic-gate 	}
936*0Sstevel@tonic-gate 	return (0);
937*0Sstevel@tonic-gate }
938*0Sstevel@tonic-gate 
939*0Sstevel@tonic-gate /*
940*0Sstevel@tonic-gate  * Utility function handles reducing the length of a devmap handle during unmap
941*0Sstevel@tonic-gate  * Note that is only used for unmapping the front portion of the handler,
942*0Sstevel@tonic-gate  * i.e., we are bumping up the offset/pfn etc up by len
943*0Sstevel@tonic-gate  * Do not use if reducing length at the tail.
944*0Sstevel@tonic-gate  */
945*0Sstevel@tonic-gate static void
946*0Sstevel@tonic-gate devmap_handle_reduce_len(devmap_handle_t *dhp, size_t len)
947*0Sstevel@tonic-gate {
948*0Sstevel@tonic-gate 	struct ddi_umem_cookie *cp;
949*0Sstevel@tonic-gate 	struct devmap_pmem_cookie *pcp;
950*0Sstevel@tonic-gate 	/*
951*0Sstevel@tonic-gate 	 * adjust devmap handle fields
952*0Sstevel@tonic-gate 	 */
953*0Sstevel@tonic-gate 	ASSERT(len < dhp->dh_len);
954*0Sstevel@tonic-gate 
955*0Sstevel@tonic-gate 	/* Make sure only page-aligned changes are done */
956*0Sstevel@tonic-gate 	ASSERT((len & PAGEOFFSET) == 0);
957*0Sstevel@tonic-gate 
958*0Sstevel@tonic-gate 	dhp->dh_len -= len;
959*0Sstevel@tonic-gate 	dhp->dh_uoff += (offset_t)len;
960*0Sstevel@tonic-gate 	dhp->dh_roff += (offset_t)len;
961*0Sstevel@tonic-gate 	dhp->dh_uvaddr += len;
962*0Sstevel@tonic-gate 	/* Need to grab dhp lock if REMAP */
963*0Sstevel@tonic-gate 	HOLD_DHP_LOCK(dhp);
964*0Sstevel@tonic-gate 	cp = dhp->dh_cookie;
965*0Sstevel@tonic-gate 	if (!(dhp->dh_flags & DEVMAP_MAPPING_INVALID)) {
966*0Sstevel@tonic-gate 		if (cookie_is_devmem(cp)) {
967*0Sstevel@tonic-gate 			dhp->dh_pfn += btop(len);
968*0Sstevel@tonic-gate 		} else if (cookie_is_pmem(cp)) {
969*0Sstevel@tonic-gate 			pcp = (struct devmap_pmem_cookie *)dhp->dh_pcookie;
970*0Sstevel@tonic-gate 			ASSERT((dhp->dh_roff & PAGEOFFSET) == 0 &&
971*0Sstevel@tonic-gate 				dhp->dh_roff < ptob(pcp->dp_npages));
972*0Sstevel@tonic-gate 		} else {
973*0Sstevel@tonic-gate 			ASSERT(dhp->dh_roff < cp->size);
974*0Sstevel@tonic-gate 			ASSERT(dhp->dh_cvaddr >= cp->cvaddr &&
975*0Sstevel@tonic-gate 				dhp->dh_cvaddr < (cp->cvaddr + cp->size));
976*0Sstevel@tonic-gate 			ASSERT((dhp->dh_cvaddr + len) <=
977*0Sstevel@tonic-gate 				(cp->cvaddr + cp->size));
978*0Sstevel@tonic-gate 
979*0Sstevel@tonic-gate 			dhp->dh_cvaddr += len;
980*0Sstevel@tonic-gate 		}
981*0Sstevel@tonic-gate 	}
982*0Sstevel@tonic-gate 	/* XXX - Should recalculate the DEVMAP_FLAG_LARGE after changes */
983*0Sstevel@tonic-gate 	RELE_DHP_LOCK(dhp);
984*0Sstevel@tonic-gate }
985*0Sstevel@tonic-gate 
986*0Sstevel@tonic-gate /*
987*0Sstevel@tonic-gate  * Free devmap handle, dhp.
988*0Sstevel@tonic-gate  * Return the next devmap handle on the linked list.
989*0Sstevel@tonic-gate  */
990*0Sstevel@tonic-gate static devmap_handle_t *
991*0Sstevel@tonic-gate devmap_handle_unmap(devmap_handle_t *dhp)
992*0Sstevel@tonic-gate {
993*0Sstevel@tonic-gate 	struct devmap_callback_ctl *callbackops = &dhp->dh_callbackops;
994*0Sstevel@tonic-gate 	struct segdev_data *sdp = (struct segdev_data *)dhp->dh_seg->s_data;
995*0Sstevel@tonic-gate 	devmap_handle_t *dhpp = (devmap_handle_t *)sdp->devmap_data;
996*0Sstevel@tonic-gate 
997*0Sstevel@tonic-gate 	ASSERT(dhp != NULL);
998*0Sstevel@tonic-gate 
999*0Sstevel@tonic-gate 	/*
1000*0Sstevel@tonic-gate 	 * before we free up dhp, call the driver's devmap_unmap entry point
1001*0Sstevel@tonic-gate 	 * to free resources allocated for this dhp.
1002*0Sstevel@tonic-gate 	 */
1003*0Sstevel@tonic-gate 	if (callbackops->devmap_unmap != NULL) {
1004*0Sstevel@tonic-gate 		(*callbackops->devmap_unmap)(dhp, dhp->dh_pvtp, dhp->dh_uoff,
1005*0Sstevel@tonic-gate 			dhp->dh_len, NULL, NULL, NULL, NULL);
1006*0Sstevel@tonic-gate 	}
1007*0Sstevel@tonic-gate 
1008*0Sstevel@tonic-gate 	if (dhpp == dhp) {	/* releasing first dhp, change sdp data */
1009*0Sstevel@tonic-gate 		sdp->devmap_data = dhp->dh_next;
1010*0Sstevel@tonic-gate 	} else {
1011*0Sstevel@tonic-gate 		while (dhpp->dh_next != dhp) {
1012*0Sstevel@tonic-gate 			dhpp = dhpp->dh_next;
1013*0Sstevel@tonic-gate 		}
1014*0Sstevel@tonic-gate 		dhpp->dh_next = dhp->dh_next;
1015*0Sstevel@tonic-gate 	}
1016*0Sstevel@tonic-gate 	dhpp = dhp->dh_next;	/* return value is next dhp in chain */
1017*0Sstevel@tonic-gate 
1018*0Sstevel@tonic-gate 	if (dhp->dh_softlock != NULL)
1019*0Sstevel@tonic-gate 		devmap_softlock_rele(dhp);
1020*0Sstevel@tonic-gate 
1021*0Sstevel@tonic-gate 	if (dhp->dh_ctx != NULL)
1022*0Sstevel@tonic-gate 		devmap_ctx_rele(dhp);
1023*0Sstevel@tonic-gate 
1024*0Sstevel@tonic-gate 	if (dhp->dh_flags & DEVMAP_LOCK_INITED) {
1025*0Sstevel@tonic-gate 		mutex_destroy(&dhp->dh_lock);
1026*0Sstevel@tonic-gate 	}
1027*0Sstevel@tonic-gate 	kmem_free(dhp, sizeof (devmap_handle_t));
1028*0Sstevel@tonic-gate 
1029*0Sstevel@tonic-gate 	return (dhpp);
1030*0Sstevel@tonic-gate }
1031*0Sstevel@tonic-gate 
1032*0Sstevel@tonic-gate /*
1033*0Sstevel@tonic-gate  * Free complete devmap handles from dhp for len bytes
1034*0Sstevel@tonic-gate  * dhp can be either the first handle or a subsequent handle
1035*0Sstevel@tonic-gate  */
1036*0Sstevel@tonic-gate static void
1037*0Sstevel@tonic-gate devmap_handle_unmap_head(devmap_handle_t *dhp, size_t len)
1038*0Sstevel@tonic-gate {
1039*0Sstevel@tonic-gate 	struct devmap_callback_ctl *callbackops;
1040*0Sstevel@tonic-gate 
1041*0Sstevel@tonic-gate 	/*
1042*0Sstevel@tonic-gate 	 * free the devmap handles covered by len.
1043*0Sstevel@tonic-gate 	 */
1044*0Sstevel@tonic-gate 	while (len >= dhp->dh_len) {
1045*0Sstevel@tonic-gate 		len -= dhp->dh_len;
1046*0Sstevel@tonic-gate 		dhp = devmap_handle_unmap(dhp);
1047*0Sstevel@tonic-gate 	}
1048*0Sstevel@tonic-gate 	if (len != 0) {	/* partial unmap at head of first remaining dhp */
1049*0Sstevel@tonic-gate 		callbackops = &dhp->dh_callbackops;
1050*0Sstevel@tonic-gate 
1051*0Sstevel@tonic-gate 		/*
1052*0Sstevel@tonic-gate 		 * Call the unmap callback so the drivers can make
1053*0Sstevel@tonic-gate 		 * adjustment on its private data.
1054*0Sstevel@tonic-gate 		 */
1055*0Sstevel@tonic-gate 		if (callbackops->devmap_unmap != NULL)
1056*0Sstevel@tonic-gate 			(*callbackops->devmap_unmap)(dhp, dhp->dh_pvtp,
1057*0Sstevel@tonic-gate 			    dhp->dh_uoff, len, NULL, NULL, dhp, &dhp->dh_pvtp);
1058*0Sstevel@tonic-gate 		devmap_handle_reduce_len(dhp, len);
1059*0Sstevel@tonic-gate 	}
1060*0Sstevel@tonic-gate }
1061*0Sstevel@tonic-gate 
1062*0Sstevel@tonic-gate /*
1063*0Sstevel@tonic-gate  * Free devmap handles to truncate  the mapping after addr
1064*0Sstevel@tonic-gate  * RFE: Simpler to pass in dhp pointing at correct dhp (avoid find again)
1065*0Sstevel@tonic-gate  *	Also could then use the routine in middle unmap case too
1066*0Sstevel@tonic-gate  */
1067*0Sstevel@tonic-gate static void
1068*0Sstevel@tonic-gate devmap_handle_unmap_tail(devmap_handle_t *dhp, caddr_t addr)
1069*0Sstevel@tonic-gate {
1070*0Sstevel@tonic-gate 	register struct seg *seg = dhp->dh_seg;
1071*0Sstevel@tonic-gate 	register struct segdev_data *sdp = (struct segdev_data *)seg->s_data;
1072*0Sstevel@tonic-gate 	register devmap_handle_t *dhph = (devmap_handle_t *)sdp->devmap_data;
1073*0Sstevel@tonic-gate 	struct devmap_callback_ctl *callbackops;
1074*0Sstevel@tonic-gate 	register devmap_handle_t *dhpp;
1075*0Sstevel@tonic-gate 	size_t maplen;
1076*0Sstevel@tonic-gate 	ulong_t off;
1077*0Sstevel@tonic-gate 	size_t len;
1078*0Sstevel@tonic-gate 
1079*0Sstevel@tonic-gate 	maplen = (size_t)(addr - dhp->dh_uvaddr);
1080*0Sstevel@tonic-gate 	dhph = devmap_find_handle(dhph, addr);
1081*0Sstevel@tonic-gate 
1082*0Sstevel@tonic-gate 	while (dhph != NULL) {
1083*0Sstevel@tonic-gate 		if (maplen == 0) {
1084*0Sstevel@tonic-gate 			dhph =  devmap_handle_unmap(dhph);
1085*0Sstevel@tonic-gate 		} else {
1086*0Sstevel@tonic-gate 			callbackops = &dhph->dh_callbackops;
1087*0Sstevel@tonic-gate 			len = dhph->dh_len - maplen;
1088*0Sstevel@tonic-gate 			off = (ulong_t)sdp->offset + (addr - seg->s_base);
1089*0Sstevel@tonic-gate 			/*
1090*0Sstevel@tonic-gate 			 * Call the unmap callback so the driver
1091*0Sstevel@tonic-gate 			 * can make adjustments on its private data.
1092*0Sstevel@tonic-gate 			 */
1093*0Sstevel@tonic-gate 			if (callbackops->devmap_unmap != NULL)
1094*0Sstevel@tonic-gate 				(*callbackops->devmap_unmap)(dhph,
1095*0Sstevel@tonic-gate 					dhph->dh_pvtp, off, len,
1096*0Sstevel@tonic-gate 					(devmap_cookie_t *)dhph,
1097*0Sstevel@tonic-gate 					&dhph->dh_pvtp, NULL, NULL);
1098*0Sstevel@tonic-gate 			/* XXX Reducing len needs to recalculate LARGE flag */
1099*0Sstevel@tonic-gate 			dhph->dh_len = maplen;
1100*0Sstevel@tonic-gate 			maplen = 0;
1101*0Sstevel@tonic-gate 			dhpp = dhph->dh_next;
1102*0Sstevel@tonic-gate 			dhph->dh_next = NULL;
1103*0Sstevel@tonic-gate 			dhph = dhpp;
1104*0Sstevel@tonic-gate 		}
1105*0Sstevel@tonic-gate 	} /* end while */
1106*0Sstevel@tonic-gate }
1107*0Sstevel@tonic-gate 
1108*0Sstevel@tonic-gate /*
1109*0Sstevel@tonic-gate  * Free a segment.
1110*0Sstevel@tonic-gate  */
1111*0Sstevel@tonic-gate static void
1112*0Sstevel@tonic-gate segdev_free(struct seg *seg)
1113*0Sstevel@tonic-gate {
1114*0Sstevel@tonic-gate 	register struct segdev_data *sdp = (struct segdev_data *)seg->s_data;
1115*0Sstevel@tonic-gate 	devmap_handle_t *dhp = (devmap_handle_t *)sdp->devmap_data;
1116*0Sstevel@tonic-gate 
1117*0Sstevel@tonic-gate 	TRACE_2(TR_FAC_DEVMAP, TR_DEVMAP_FREE,
1118*0Sstevel@tonic-gate 	    "segdev_free: dhp=%p seg=%p", (void *)dhp, (void *)seg);
1119*0Sstevel@tonic-gate 	DEBUGF(3, (CE_CONT, "segdev_free: dhp %p seg %p\n",
1120*0Sstevel@tonic-gate 	    (void *)dhp, (void *)seg));
1121*0Sstevel@tonic-gate 
1122*0Sstevel@tonic-gate 	/*
1123*0Sstevel@tonic-gate 	 * Since the address space is "write" locked, we
1124*0Sstevel@tonic-gate 	 * don't need the segment lock to protect "segdev" data.
1125*0Sstevel@tonic-gate 	 */
1126*0Sstevel@tonic-gate 	ASSERT(seg->s_as && AS_WRITE_HELD(seg->s_as, &seg->s_as->a_lock));
1127*0Sstevel@tonic-gate 
1128*0Sstevel@tonic-gate 	while (dhp != NULL)
1129*0Sstevel@tonic-gate 		dhp = devmap_handle_unmap(dhp);
1130*0Sstevel@tonic-gate 
1131*0Sstevel@tonic-gate 	VN_RELE(sdp->vp);
1132*0Sstevel@tonic-gate 	if (sdp->vpage != NULL)
1133*0Sstevel@tonic-gate 		kmem_free(sdp->vpage, vpgtob(seg_pages(seg)));
1134*0Sstevel@tonic-gate 
1135*0Sstevel@tonic-gate 	mutex_destroy(&sdp->lock);
1136*0Sstevel@tonic-gate 	kmem_free(sdp, sizeof (*sdp));
1137*0Sstevel@tonic-gate }
1138*0Sstevel@tonic-gate 
1139*0Sstevel@tonic-gate static void
1140*0Sstevel@tonic-gate free_devmap_handle(devmap_handle_t *dhp)
1141*0Sstevel@tonic-gate {
1142*0Sstevel@tonic-gate 	register devmap_handle_t *dhpp;
1143*0Sstevel@tonic-gate 
1144*0Sstevel@tonic-gate 	/*
1145*0Sstevel@tonic-gate 	 * free up devmap handle
1146*0Sstevel@tonic-gate 	 */
1147*0Sstevel@tonic-gate 	while (dhp != NULL) {
1148*0Sstevel@tonic-gate 		dhpp = dhp->dh_next;
1149*0Sstevel@tonic-gate 		if (dhp->dh_flags & DEVMAP_LOCK_INITED) {
1150*0Sstevel@tonic-gate 			mutex_destroy(&dhp->dh_lock);
1151*0Sstevel@tonic-gate 		}
1152*0Sstevel@tonic-gate 
1153*0Sstevel@tonic-gate 		if (dhp->dh_softlock != NULL)
1154*0Sstevel@tonic-gate 			devmap_softlock_rele(dhp);
1155*0Sstevel@tonic-gate 
1156*0Sstevel@tonic-gate 		if (dhp->dh_ctx != NULL)
1157*0Sstevel@tonic-gate 			devmap_ctx_rele(dhp);
1158*0Sstevel@tonic-gate 
1159*0Sstevel@tonic-gate 		kmem_free(dhp, sizeof (devmap_handle_t));
1160*0Sstevel@tonic-gate 		dhp = dhpp;
1161*0Sstevel@tonic-gate 	}
1162*0Sstevel@tonic-gate }
1163*0Sstevel@tonic-gate 
1164*0Sstevel@tonic-gate /*
1165*0Sstevel@tonic-gate  * routines to lock and unlock underlying segkp segment for
1166*0Sstevel@tonic-gate  * KMEM_PAGEABLE type cookies.
1167*0Sstevel@tonic-gate  * segkp only allows a single pending F_SOFTLOCK
1168*0Sstevel@tonic-gate  * we keep track of number of locks in the cookie so we can
1169*0Sstevel@tonic-gate  * have multiple pending faults and manage the calls to segkp.
1170*0Sstevel@tonic-gate  * RFE: if segkp supports either pagelock or can support multiple
1171*0Sstevel@tonic-gate  * calls to F_SOFTLOCK, then these routines can go away.
1172*0Sstevel@tonic-gate  *	If pagelock, segdev_faultpage can fault on a page by page basis
1173*0Sstevel@tonic-gate  *		and simplifies the code quite a bit.
1174*0Sstevel@tonic-gate  *	if multiple calls allowed but not partial ranges, then need for
1175*0Sstevel@tonic-gate  *	cookie->lock and locked count goes away, code can call as_fault directly
1176*0Sstevel@tonic-gate  */
1177*0Sstevel@tonic-gate static faultcode_t
1178*0Sstevel@tonic-gate acquire_kpmem_lock(struct ddi_umem_cookie *cookie, size_t npages)
1179*0Sstevel@tonic-gate {
1180*0Sstevel@tonic-gate 	int err = 0;
1181*0Sstevel@tonic-gate 	ASSERT(cookie_is_kpmem(cookie));
1182*0Sstevel@tonic-gate 	/*
1183*0Sstevel@tonic-gate 	 * Fault in pages in segkp with F_SOFTLOCK.
1184*0Sstevel@tonic-gate 	 * We want to hold the lock until all pages have been loaded.
1185*0Sstevel@tonic-gate 	 * segkp only allows single caller to hold SOFTLOCK, so cookie
1186*0Sstevel@tonic-gate 	 * holds a count so we dont call into segkp multiple times
1187*0Sstevel@tonic-gate 	 */
1188*0Sstevel@tonic-gate 	mutex_enter(&cookie->lock);
1189*0Sstevel@tonic-gate 
1190*0Sstevel@tonic-gate 	/*
1191*0Sstevel@tonic-gate 	 * Check for overflow in locked field
1192*0Sstevel@tonic-gate 	 */
1193*0Sstevel@tonic-gate 	if ((UINT32_MAX - cookie->locked) < npages) {
1194*0Sstevel@tonic-gate 		err = FC_MAKE_ERR(ENOMEM);
1195*0Sstevel@tonic-gate 	} else if (cookie->locked == 0) {
1196*0Sstevel@tonic-gate 		/* First time locking */
1197*0Sstevel@tonic-gate 		err = as_fault(kas.a_hat, &kas, cookie->cvaddr,
1198*0Sstevel@tonic-gate 		    cookie->size, F_SOFTLOCK, PROT_READ|PROT_WRITE);
1199*0Sstevel@tonic-gate 	}
1200*0Sstevel@tonic-gate 	if (!err) {
1201*0Sstevel@tonic-gate 		cookie->locked += npages;
1202*0Sstevel@tonic-gate 	}
1203*0Sstevel@tonic-gate 	mutex_exit(&cookie->lock);
1204*0Sstevel@tonic-gate 	return (err);
1205*0Sstevel@tonic-gate }
1206*0Sstevel@tonic-gate 
1207*0Sstevel@tonic-gate static void
1208*0Sstevel@tonic-gate release_kpmem_lock(struct ddi_umem_cookie *cookie, size_t npages)
1209*0Sstevel@tonic-gate {
1210*0Sstevel@tonic-gate 	mutex_enter(&cookie->lock);
1211*0Sstevel@tonic-gate 	ASSERT(cookie_is_kpmem(cookie));
1212*0Sstevel@tonic-gate 	ASSERT(cookie->locked >= npages);
1213*0Sstevel@tonic-gate 	cookie->locked -= (uint_t)npages;
1214*0Sstevel@tonic-gate 	if (cookie->locked == 0) {
1215*0Sstevel@tonic-gate 		/* Last unlock */
1216*0Sstevel@tonic-gate 		if (as_fault(kas.a_hat, &kas, cookie->cvaddr,
1217*0Sstevel@tonic-gate 		    cookie->size, F_SOFTUNLOCK, PROT_READ|PROT_WRITE))
1218*0Sstevel@tonic-gate 			panic("segdev releasing kpmem lock %p", (void *)cookie);
1219*0Sstevel@tonic-gate 	}
1220*0Sstevel@tonic-gate 	mutex_exit(&cookie->lock);
1221*0Sstevel@tonic-gate }
1222*0Sstevel@tonic-gate 
1223*0Sstevel@tonic-gate /*
1224*0Sstevel@tonic-gate  * Routines to synchronize F_SOFTLOCK and F_INVAL faults for
1225*0Sstevel@tonic-gate  * drivers with devmap_access callbacks
1226*0Sstevel@tonic-gate  * slock->softlocked basically works like a rw lock
1227*0Sstevel@tonic-gate  *	-ve counts => F_SOFTLOCK in progress
1228*0Sstevel@tonic-gate  *	+ve counts => F_INVAL/F_PROT in progress
1229*0Sstevel@tonic-gate  * We allow only one F_SOFTLOCK at a time
1230*0Sstevel@tonic-gate  * but can have multiple pending F_INVAL/F_PROT calls
1231*0Sstevel@tonic-gate  *
1232*0Sstevel@tonic-gate  * This routine waits using cv_wait_sig so killing processes is more graceful
1233*0Sstevel@tonic-gate  * Returns EINTR if coming out of this routine due to a signal, 0 otherwise
1234*0Sstevel@tonic-gate  */
1235*0Sstevel@tonic-gate static int devmap_softlock_enter(
1236*0Sstevel@tonic-gate 	struct devmap_softlock *slock,
1237*0Sstevel@tonic-gate 	size_t npages,
1238*0Sstevel@tonic-gate 	enum fault_type type)
1239*0Sstevel@tonic-gate {
1240*0Sstevel@tonic-gate 	if (npages == 0)
1241*0Sstevel@tonic-gate 		return (0);
1242*0Sstevel@tonic-gate 	mutex_enter(&(slock->lock));
1243*0Sstevel@tonic-gate 	switch (type) {
1244*0Sstevel@tonic-gate 	case F_SOFTLOCK :
1245*0Sstevel@tonic-gate 		while (slock->softlocked) {
1246*0Sstevel@tonic-gate 			if (cv_wait_sig(&(slock)->cv, &(slock)->lock) == 0) {
1247*0Sstevel@tonic-gate 				/* signalled */
1248*0Sstevel@tonic-gate 				mutex_exit(&(slock->lock));
1249*0Sstevel@tonic-gate 				return (EINTR);
1250*0Sstevel@tonic-gate 			}
1251*0Sstevel@tonic-gate 		}
1252*0Sstevel@tonic-gate 		slock->softlocked -= npages; /* -ve count => locked */
1253*0Sstevel@tonic-gate 		break;
1254*0Sstevel@tonic-gate 	case F_INVAL :
1255*0Sstevel@tonic-gate 	case F_PROT :
1256*0Sstevel@tonic-gate 		while (slock->softlocked < 0)
1257*0Sstevel@tonic-gate 			if (cv_wait_sig(&(slock)->cv, &(slock)->lock) == 0) {
1258*0Sstevel@tonic-gate 				/* signalled */
1259*0Sstevel@tonic-gate 				mutex_exit(&(slock->lock));
1260*0Sstevel@tonic-gate 				return (EINTR);
1261*0Sstevel@tonic-gate 			}
1262*0Sstevel@tonic-gate 		slock->softlocked += npages; /* +ve count => f_invals */
1263*0Sstevel@tonic-gate 		break;
1264*0Sstevel@tonic-gate 	default:
1265*0Sstevel@tonic-gate 		ASSERT(0);
1266*0Sstevel@tonic-gate 	}
1267*0Sstevel@tonic-gate 	mutex_exit(&(slock->lock));
1268*0Sstevel@tonic-gate 	return (0);
1269*0Sstevel@tonic-gate }
1270*0Sstevel@tonic-gate 
1271*0Sstevel@tonic-gate static void devmap_softlock_exit(
1272*0Sstevel@tonic-gate 	struct devmap_softlock *slock,
1273*0Sstevel@tonic-gate 	size_t npages,
1274*0Sstevel@tonic-gate 	enum fault_type type)
1275*0Sstevel@tonic-gate {
1276*0Sstevel@tonic-gate 	if (slock == NULL)
1277*0Sstevel@tonic-gate 		return;
1278*0Sstevel@tonic-gate 	mutex_enter(&(slock->lock));
1279*0Sstevel@tonic-gate 	switch (type) {
1280*0Sstevel@tonic-gate 	case F_SOFTLOCK :
1281*0Sstevel@tonic-gate 		ASSERT(-slock->softlocked >= npages);
1282*0Sstevel@tonic-gate 		slock->softlocked += npages;	/* -ve count is softlocked */
1283*0Sstevel@tonic-gate 		if (slock->softlocked == 0)
1284*0Sstevel@tonic-gate 			cv_signal(&slock->cv);
1285*0Sstevel@tonic-gate 		break;
1286*0Sstevel@tonic-gate 	case F_INVAL :
1287*0Sstevel@tonic-gate 	case F_PROT:
1288*0Sstevel@tonic-gate 		ASSERT(slock->softlocked >= npages);
1289*0Sstevel@tonic-gate 		slock->softlocked -= npages;
1290*0Sstevel@tonic-gate 		if (slock->softlocked == 0)
1291*0Sstevel@tonic-gate 			cv_signal(&slock->cv);
1292*0Sstevel@tonic-gate 		break;
1293*0Sstevel@tonic-gate 	default:
1294*0Sstevel@tonic-gate 		ASSERT(0);
1295*0Sstevel@tonic-gate 	}
1296*0Sstevel@tonic-gate 	mutex_exit(&(slock->lock));
1297*0Sstevel@tonic-gate }
1298*0Sstevel@tonic-gate 
1299*0Sstevel@tonic-gate /*
1300*0Sstevel@tonic-gate  * Do a F_SOFTUNLOCK call over the range requested.
1301*0Sstevel@tonic-gate  * The range must have already been F_SOFTLOCK'ed.
1302*0Sstevel@tonic-gate  * The segment lock should be held, (but not the segment private lock?)
1303*0Sstevel@tonic-gate  *  The softunlock code below does not adjust for large page sizes
1304*0Sstevel@tonic-gate  *	assumes the caller already did any addr/len adjustments for
1305*0Sstevel@tonic-gate  *	pagesize mappings before calling.
1306*0Sstevel@tonic-gate  */
1307*0Sstevel@tonic-gate /*ARGSUSED*/
1308*0Sstevel@tonic-gate static void
1309*0Sstevel@tonic-gate segdev_softunlock(
1310*0Sstevel@tonic-gate 	struct hat *hat,		/* the hat */
1311*0Sstevel@tonic-gate 	struct seg *seg,		/* seg_dev of interest */
1312*0Sstevel@tonic-gate 	caddr_t addr,			/* base address of range */
1313*0Sstevel@tonic-gate 	size_t len,			/* number of bytes */
1314*0Sstevel@tonic-gate 	enum seg_rw rw)			/* type of access at fault */
1315*0Sstevel@tonic-gate {
1316*0Sstevel@tonic-gate 	struct segdev_data *sdp = (struct segdev_data *)seg->s_data;
1317*0Sstevel@tonic-gate 	devmap_handle_t *dhp_head = (devmap_handle_t *)sdp->devmap_data;
1318*0Sstevel@tonic-gate 
1319*0Sstevel@tonic-gate 	TRACE_4(TR_FAC_DEVMAP, TR_DEVMAP_SOFTUNLOCK,
1320*0Sstevel@tonic-gate 	    "segdev_softunlock:dhp_head=%p sdp=%p addr=%p len=%lx",
1321*0Sstevel@tonic-gate 	    dhp_head, sdp, addr, len);
1322*0Sstevel@tonic-gate 	DEBUGF(3, (CE_CONT, "segdev_softunlock: dhp %p lockcnt %lx "
1323*0Sstevel@tonic-gate 	    "addr %p len %lx\n",
1324*0Sstevel@tonic-gate 	    (void *)dhp_head, sdp->softlockcnt, (void *)addr, len));
1325*0Sstevel@tonic-gate 
1326*0Sstevel@tonic-gate 	hat_unlock(hat, addr, len);
1327*0Sstevel@tonic-gate 
1328*0Sstevel@tonic-gate 	if (dhp_head != NULL) {
1329*0Sstevel@tonic-gate 		devmap_handle_t *dhp;
1330*0Sstevel@tonic-gate 		size_t mlen;
1331*0Sstevel@tonic-gate 		ulong_t off;
1332*0Sstevel@tonic-gate 
1333*0Sstevel@tonic-gate 		dhp = devmap_find_handle(dhp_head, addr);
1334*0Sstevel@tonic-gate 		ASSERT(dhp != NULL);
1335*0Sstevel@tonic-gate 
1336*0Sstevel@tonic-gate 		off = (ulong_t)(addr - dhp->dh_uvaddr);
1337*0Sstevel@tonic-gate 		while (len != 0) {
1338*0Sstevel@tonic-gate 			mlen = MIN(len, (dhp->dh_len - off));
1339*0Sstevel@tonic-gate 
1340*0Sstevel@tonic-gate 			/*
1341*0Sstevel@tonic-gate 			 * unlock segkp memory, locked during F_SOFTLOCK
1342*0Sstevel@tonic-gate 			 */
1343*0Sstevel@tonic-gate 			if (dhp_is_kpmem(dhp)) {
1344*0Sstevel@tonic-gate 				release_kpmem_lock(
1345*0Sstevel@tonic-gate 				    (struct ddi_umem_cookie *)dhp->dh_cookie,
1346*0Sstevel@tonic-gate 				    btopr(mlen));
1347*0Sstevel@tonic-gate 			}
1348*0Sstevel@tonic-gate 
1349*0Sstevel@tonic-gate 			/*
1350*0Sstevel@tonic-gate 			 * Do the softlock accounting for devmap_access
1351*0Sstevel@tonic-gate 			 */
1352*0Sstevel@tonic-gate 			if (dhp->dh_callbackops.devmap_access != NULL) {
1353*0Sstevel@tonic-gate 				devmap_softlock_exit(dhp->dh_softlock,
1354*0Sstevel@tonic-gate 					btopr(mlen), F_SOFTLOCK);
1355*0Sstevel@tonic-gate 			}
1356*0Sstevel@tonic-gate 
1357*0Sstevel@tonic-gate 			len -= mlen;
1358*0Sstevel@tonic-gate 			dhp = dhp->dh_next;
1359*0Sstevel@tonic-gate 			off = 0;
1360*0Sstevel@tonic-gate 		}
1361*0Sstevel@tonic-gate 	}
1362*0Sstevel@tonic-gate 
1363*0Sstevel@tonic-gate 	mutex_enter(&freemem_lock);
1364*0Sstevel@tonic-gate 	ASSERT(sdp->softlockcnt >= btopr(len));
1365*0Sstevel@tonic-gate 	sdp->softlockcnt -= btopr(len);
1366*0Sstevel@tonic-gate 	mutex_exit(&freemem_lock);
1367*0Sstevel@tonic-gate 	if (sdp->softlockcnt == 0) {
1368*0Sstevel@tonic-gate 		/*
1369*0Sstevel@tonic-gate 		 * All SOFTLOCKS are gone. Wakeup any waiting
1370*0Sstevel@tonic-gate 		 * unmappers so they can try again to unmap.
1371*0Sstevel@tonic-gate 		 * Check for waiters first without the mutex
1372*0Sstevel@tonic-gate 		 * held so we don't always grab the mutex on
1373*0Sstevel@tonic-gate 		 * softunlocks.
1374*0Sstevel@tonic-gate 		 */
1375*0Sstevel@tonic-gate 		if (AS_ISUNMAPWAIT(seg->s_as)) {
1376*0Sstevel@tonic-gate 			mutex_enter(&seg->s_as->a_contents);
1377*0Sstevel@tonic-gate 			if (AS_ISUNMAPWAIT(seg->s_as)) {
1378*0Sstevel@tonic-gate 				AS_CLRUNMAPWAIT(seg->s_as);
1379*0Sstevel@tonic-gate 				cv_broadcast(&seg->s_as->a_cv);
1380*0Sstevel@tonic-gate 			}
1381*0Sstevel@tonic-gate 			mutex_exit(&seg->s_as->a_contents);
1382*0Sstevel@tonic-gate 		}
1383*0Sstevel@tonic-gate 	}
1384*0Sstevel@tonic-gate 
1385*0Sstevel@tonic-gate }
1386*0Sstevel@tonic-gate 
1387*0Sstevel@tonic-gate /*
1388*0Sstevel@tonic-gate  * Handle fault for a single page.
1389*0Sstevel@tonic-gate  * Done in a separate routine so we can handle errors more easily.
1390*0Sstevel@tonic-gate  * This routine is called only from segdev_faultpages()
1391*0Sstevel@tonic-gate  * when looping over the range of addresses requested. The segment lock is held.
1392*0Sstevel@tonic-gate  */
1393*0Sstevel@tonic-gate static faultcode_t
1394*0Sstevel@tonic-gate segdev_faultpage(
1395*0Sstevel@tonic-gate 	struct hat *hat,		/* the hat */
1396*0Sstevel@tonic-gate 	struct seg *seg,		/* seg_dev of interest */
1397*0Sstevel@tonic-gate 	caddr_t addr,			/* address in as */
1398*0Sstevel@tonic-gate 	struct vpage *vpage,		/* pointer to vpage for seg, addr */
1399*0Sstevel@tonic-gate 	enum fault_type type,		/* type of fault */
1400*0Sstevel@tonic-gate 	enum seg_rw rw,			/* type of access at fault */
1401*0Sstevel@tonic-gate 	devmap_handle_t *dhp)		/* devmap handle if any for this page */
1402*0Sstevel@tonic-gate {
1403*0Sstevel@tonic-gate 	struct segdev_data *sdp = (struct segdev_data *)seg->s_data;
1404*0Sstevel@tonic-gate 	uint_t prot;
1405*0Sstevel@tonic-gate 	pfn_t pfnum = PFN_INVALID;
1406*0Sstevel@tonic-gate 	u_offset_t offset;
1407*0Sstevel@tonic-gate 	uint_t hat_flags;
1408*0Sstevel@tonic-gate 	dev_info_t *dip;
1409*0Sstevel@tonic-gate 
1410*0Sstevel@tonic-gate 	TRACE_3(TR_FAC_DEVMAP, TR_DEVMAP_FAULTPAGE,
1411*0Sstevel@tonic-gate 	    "segdev_faultpage: dhp=%p seg=%p addr=%p", dhp, seg, addr);
1412*0Sstevel@tonic-gate 	DEBUGF(8, (CE_CONT, "segdev_faultpage: dhp %p seg %p addr %p \n",
1413*0Sstevel@tonic-gate 	    (void *)dhp, (void *)seg, (void *)addr));
1414*0Sstevel@tonic-gate 
1415*0Sstevel@tonic-gate 	/*
1416*0Sstevel@tonic-gate 	 * Initialize protection value for this page.
1417*0Sstevel@tonic-gate 	 * If we have per page protection values check it now.
1418*0Sstevel@tonic-gate 	 */
1419*0Sstevel@tonic-gate 	if (sdp->pageprot) {
1420*0Sstevel@tonic-gate 		uint_t protchk;
1421*0Sstevel@tonic-gate 
1422*0Sstevel@tonic-gate 		switch (rw) {
1423*0Sstevel@tonic-gate 		case S_READ:
1424*0Sstevel@tonic-gate 			protchk = PROT_READ;
1425*0Sstevel@tonic-gate 			break;
1426*0Sstevel@tonic-gate 		case S_WRITE:
1427*0Sstevel@tonic-gate 			protchk = PROT_WRITE;
1428*0Sstevel@tonic-gate 			break;
1429*0Sstevel@tonic-gate 		case S_EXEC:
1430*0Sstevel@tonic-gate 			protchk = PROT_EXEC;
1431*0Sstevel@tonic-gate 			break;
1432*0Sstevel@tonic-gate 		case S_OTHER:
1433*0Sstevel@tonic-gate 		default:
1434*0Sstevel@tonic-gate 			protchk = PROT_READ | PROT_WRITE | PROT_EXEC;
1435*0Sstevel@tonic-gate 			break;
1436*0Sstevel@tonic-gate 		}
1437*0Sstevel@tonic-gate 
1438*0Sstevel@tonic-gate 		prot = VPP_PROT(vpage);
1439*0Sstevel@tonic-gate 		if ((prot & protchk) == 0)
1440*0Sstevel@tonic-gate 			return (FC_PROT);	/* illegal access type */
1441*0Sstevel@tonic-gate 	} else {
1442*0Sstevel@tonic-gate 		prot = sdp->prot;
1443*0Sstevel@tonic-gate 		/* caller has already done segment level protection check */
1444*0Sstevel@tonic-gate 	}
1445*0Sstevel@tonic-gate 
1446*0Sstevel@tonic-gate 	if (type == F_SOFTLOCK) {
1447*0Sstevel@tonic-gate 		mutex_enter(&freemem_lock);
1448*0Sstevel@tonic-gate 		sdp->softlockcnt++;
1449*0Sstevel@tonic-gate 		mutex_exit(&freemem_lock);
1450*0Sstevel@tonic-gate 	}
1451*0Sstevel@tonic-gate 
1452*0Sstevel@tonic-gate 	hat_flags = ((type == F_SOFTLOCK) ? HAT_LOAD_LOCK : HAT_LOAD);
1453*0Sstevel@tonic-gate 	offset = sdp->offset + (u_offset_t)(addr - seg->s_base);
1454*0Sstevel@tonic-gate 	/*
1455*0Sstevel@tonic-gate 	 * In the devmap framework, sdp->mapfunc is set to NULL.  we can get
1456*0Sstevel@tonic-gate 	 * pfnum from dhp->dh_pfn (at beginning of segment) and offset from
1457*0Sstevel@tonic-gate 	 * seg->s_base.
1458*0Sstevel@tonic-gate 	 */
1459*0Sstevel@tonic-gate 	if (dhp == NULL) {
1460*0Sstevel@tonic-gate 		/* If segment has devmap_data, then dhp should be non-NULL */
1461*0Sstevel@tonic-gate 		ASSERT(sdp->devmap_data == NULL);
1462*0Sstevel@tonic-gate 		pfnum = (pfn_t)cdev_mmap(sdp->mapfunc, sdp->vp->v_rdev,
1463*0Sstevel@tonic-gate 			(off_t)offset, prot);
1464*0Sstevel@tonic-gate 		prot |= sdp->hat_attr;
1465*0Sstevel@tonic-gate 	} else {
1466*0Sstevel@tonic-gate 		ulong_t off;
1467*0Sstevel@tonic-gate 		struct ddi_umem_cookie *cp;
1468*0Sstevel@tonic-gate 		struct devmap_pmem_cookie *pcp;
1469*0Sstevel@tonic-gate 
1470*0Sstevel@tonic-gate 		/* ensure the dhp passed in contains addr. */
1471*0Sstevel@tonic-gate 		ASSERT(dhp == devmap_find_handle(
1472*0Sstevel@tonic-gate 			(devmap_handle_t *)sdp->devmap_data, addr));
1473*0Sstevel@tonic-gate 
1474*0Sstevel@tonic-gate 		off = addr - dhp->dh_uvaddr;
1475*0Sstevel@tonic-gate 
1476*0Sstevel@tonic-gate 		/*
1477*0Sstevel@tonic-gate 		 * This routine assumes that the caller makes sure that the
1478*0Sstevel@tonic-gate 		 * fields in dhp used below are unchanged due to remap during
1479*0Sstevel@tonic-gate 		 * this call. Caller does HOLD_DHP_LOCK if neeed
1480*0Sstevel@tonic-gate 		 */
1481*0Sstevel@tonic-gate 		cp = dhp->dh_cookie;
1482*0Sstevel@tonic-gate 		if (dhp->dh_flags & DEVMAP_MAPPING_INVALID) {
1483*0Sstevel@tonic-gate 			pfnum = PFN_INVALID;
1484*0Sstevel@tonic-gate 		} else if (cookie_is_devmem(cp)) {
1485*0Sstevel@tonic-gate 			pfnum = dhp->dh_pfn + btop(off);
1486*0Sstevel@tonic-gate 		} else if (cookie_is_pmem(cp)) {
1487*0Sstevel@tonic-gate 			pcp = (struct devmap_pmem_cookie *)dhp->dh_pcookie;
1488*0Sstevel@tonic-gate 			ASSERT((dhp->dh_roff & PAGEOFFSET) == 0 &&
1489*0Sstevel@tonic-gate 				dhp->dh_roff < ptob(pcp->dp_npages));
1490*0Sstevel@tonic-gate 			pfnum = page_pptonum(
1491*0Sstevel@tonic-gate 			    pcp->dp_pparray[btop(off + dhp->dh_roff)]);
1492*0Sstevel@tonic-gate 		} else {
1493*0Sstevel@tonic-gate 			ASSERT(dhp->dh_roff < cp->size);
1494*0Sstevel@tonic-gate 			ASSERT(dhp->dh_cvaddr >= cp->cvaddr &&
1495*0Sstevel@tonic-gate 				dhp->dh_cvaddr < (cp->cvaddr + cp->size));
1496*0Sstevel@tonic-gate 			ASSERT((dhp->dh_cvaddr + off) <=
1497*0Sstevel@tonic-gate 				(cp->cvaddr + cp->size));
1498*0Sstevel@tonic-gate 			ASSERT((dhp->dh_cvaddr + off + PAGESIZE) <=
1499*0Sstevel@tonic-gate 				(cp->cvaddr + cp->size));
1500*0Sstevel@tonic-gate 
1501*0Sstevel@tonic-gate 			switch (cp->type) {
1502*0Sstevel@tonic-gate 			case UMEM_LOCKED :
1503*0Sstevel@tonic-gate 			    if (cp->pparray != NULL) {
1504*0Sstevel@tonic-gate 				ASSERT((dhp->dh_roff & PAGEOFFSET) == 0);
1505*0Sstevel@tonic-gate 				pfnum = page_pptonum(
1506*0Sstevel@tonic-gate 				    cp->pparray[btop(off + dhp->dh_roff)]);
1507*0Sstevel@tonic-gate 			    } else {
1508*0Sstevel@tonic-gate 				pfnum = hat_getpfnum(
1509*0Sstevel@tonic-gate 				    ((proc_t *)cp->procp)->p_as->a_hat,
1510*0Sstevel@tonic-gate 				    cp->cvaddr + off);
1511*0Sstevel@tonic-gate 			    }
1512*0Sstevel@tonic-gate 			    break;
1513*0Sstevel@tonic-gate 			case UMEM_TRASH :
1514*0Sstevel@tonic-gate 			    pfnum = page_pptonum(trashpp);
1515*0Sstevel@tonic-gate 			    /* We should set hat_flags to HAT_NOFAULT also */
1516*0Sstevel@tonic-gate 			    /* However, not all hat layers implement this */
1517*0Sstevel@tonic-gate 			    break;
1518*0Sstevel@tonic-gate 			case KMEM_PAGEABLE:
1519*0Sstevel@tonic-gate 			case KMEM_NON_PAGEABLE:
1520*0Sstevel@tonic-gate 			    pfnum = hat_getpfnum(kas.a_hat,
1521*0Sstevel@tonic-gate 				dhp->dh_cvaddr + off);
1522*0Sstevel@tonic-gate 			    break;
1523*0Sstevel@tonic-gate 			default :
1524*0Sstevel@tonic-gate 			    pfnum = PFN_INVALID;
1525*0Sstevel@tonic-gate 			    break;
1526*0Sstevel@tonic-gate 			}
1527*0Sstevel@tonic-gate 		}
1528*0Sstevel@tonic-gate 		prot |= dhp->dh_hat_attr;
1529*0Sstevel@tonic-gate 	}
1530*0Sstevel@tonic-gate 	if (pfnum == PFN_INVALID) {
1531*0Sstevel@tonic-gate 		return (FC_MAKE_ERR(EFAULT));
1532*0Sstevel@tonic-gate 	}
1533*0Sstevel@tonic-gate 	/* prot should already be OR'ed in with hat_attributes if needed */
1534*0Sstevel@tonic-gate 
1535*0Sstevel@tonic-gate 	TRACE_4(TR_FAC_DEVMAP, TR_DEVMAP_FAULTPAGE_CK1,
1536*0Sstevel@tonic-gate 	    "segdev_faultpage: pfnum=%lx memory=%x prot=%x flags=%x",
1537*0Sstevel@tonic-gate 	    pfnum, pf_is_memory(pfnum), prot, hat_flags);
1538*0Sstevel@tonic-gate 	DEBUGF(9, (CE_CONT, "segdev_faultpage: pfnum %lx memory %x "
1539*0Sstevel@tonic-gate 	    "prot %x flags %x\n", pfnum, pf_is_memory(pfnum), prot, hat_flags));
1540*0Sstevel@tonic-gate 
1541*0Sstevel@tonic-gate 	if (pf_is_memory(pfnum) || (dhp != NULL)) {
1542*0Sstevel@tonic-gate 		/*
1543*0Sstevel@tonic-gate 		 * It's not _really_ required here to pass sdp->hat_flags
1544*0Sstevel@tonic-gate 		 * to hat_devload even though we do it.
1545*0Sstevel@tonic-gate 		 * This is because hat figures it out DEVMEM mappings
1546*0Sstevel@tonic-gate 		 * are non-consistent, anyway.
1547*0Sstevel@tonic-gate 		 */
1548*0Sstevel@tonic-gate 		hat_devload(hat, addr, PAGESIZE, pfnum,
1549*0Sstevel@tonic-gate 				prot, hat_flags | sdp->hat_flags);
1550*0Sstevel@tonic-gate 		return (0);
1551*0Sstevel@tonic-gate 	}
1552*0Sstevel@tonic-gate 
1553*0Sstevel@tonic-gate 	/*
1554*0Sstevel@tonic-gate 	 * Fall through to the case where devmap is not used and need to call
1555*0Sstevel@tonic-gate 	 * up the device tree to set up the mapping
1556*0Sstevel@tonic-gate 	 */
1557*0Sstevel@tonic-gate 
1558*0Sstevel@tonic-gate 	dip = VTOS(VTOCVP(sdp->vp))->s_dip;
1559*0Sstevel@tonic-gate 	ASSERT(dip);
1560*0Sstevel@tonic-gate 
1561*0Sstevel@tonic-gate 	/*
1562*0Sstevel@tonic-gate 	 * When calling ddi_map_fault, we do not OR in sdp->hat_attr
1563*0Sstevel@tonic-gate 	 * This is because this calls drivers which may not expect
1564*0Sstevel@tonic-gate 	 * prot to have any other values than PROT_ALL
1565*0Sstevel@tonic-gate 	 * The root nexus driver has a hack to peek into the segment
1566*0Sstevel@tonic-gate 	 * structure and then OR in sdp->hat_attr.
1567*0Sstevel@tonic-gate 	 * XX In case the bus_ops interfaces are ever revisited
1568*0Sstevel@tonic-gate 	 * we need to fix this. prot should include other hat attributes
1569*0Sstevel@tonic-gate 	 */
1570*0Sstevel@tonic-gate 	if (ddi_map_fault(dip, hat, seg, addr, NULL, pfnum, prot & PROT_ALL,
1571*0Sstevel@tonic-gate 	    (uint_t)(type == F_SOFTLOCK)) != DDI_SUCCESS) {
1572*0Sstevel@tonic-gate 		return (FC_MAKE_ERR(EFAULT));
1573*0Sstevel@tonic-gate 	}
1574*0Sstevel@tonic-gate 	return (0);
1575*0Sstevel@tonic-gate }
1576*0Sstevel@tonic-gate 
1577*0Sstevel@tonic-gate static faultcode_t
1578*0Sstevel@tonic-gate segdev_fault(
1579*0Sstevel@tonic-gate 	struct hat *hat,		/* the hat */
1580*0Sstevel@tonic-gate 	struct seg *seg,		/* the seg_dev of interest */
1581*0Sstevel@tonic-gate 	caddr_t addr,			/* the address of the fault */
1582*0Sstevel@tonic-gate 	size_t len,			/* the length of the range */
1583*0Sstevel@tonic-gate 	enum fault_type type,		/* type of fault */
1584*0Sstevel@tonic-gate 	enum seg_rw rw)			/* type of access at fault */
1585*0Sstevel@tonic-gate {
1586*0Sstevel@tonic-gate 	struct segdev_data *sdp = (struct segdev_data *)seg->s_data;
1587*0Sstevel@tonic-gate 	devmap_handle_t *dhp_head = (devmap_handle_t *)sdp->devmap_data;
1588*0Sstevel@tonic-gate 	devmap_handle_t *dhp;
1589*0Sstevel@tonic-gate 	struct devmap_softlock *slock = NULL;
1590*0Sstevel@tonic-gate 	ulong_t slpage = 0;
1591*0Sstevel@tonic-gate 	ulong_t off;
1592*0Sstevel@tonic-gate 	caddr_t maddr = addr;
1593*0Sstevel@tonic-gate 	int err;
1594*0Sstevel@tonic-gate 	int err_is_faultcode = 0;
1595*0Sstevel@tonic-gate 
1596*0Sstevel@tonic-gate 	TRACE_5(TR_FAC_DEVMAP, TR_DEVMAP_FAULT,
1597*0Sstevel@tonic-gate 	    "segdev_fault: dhp_head=%p seg=%p addr=%p len=%lx type=%x",
1598*0Sstevel@tonic-gate 	    (void *)dhp_head, (void *)seg, (void *)addr, len, type);
1599*0Sstevel@tonic-gate 	DEBUGF(7, (CE_CONT, "segdev_fault: dhp_head %p seg %p "
1600*0Sstevel@tonic-gate 	    "addr %p len %lx type %x\n",
1601*0Sstevel@tonic-gate 	    (void *)dhp_head, (void *)seg, (void *)addr, len, type));
1602*0Sstevel@tonic-gate 
1603*0Sstevel@tonic-gate 	ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as, &seg->s_as->a_lock));
1604*0Sstevel@tonic-gate 
1605*0Sstevel@tonic-gate 	/* Handle non-devmap case */
1606*0Sstevel@tonic-gate 	if (dhp_head == NULL)
1607*0Sstevel@tonic-gate 		return (segdev_faultpages(hat, seg, addr, len, type, rw, NULL));
1608*0Sstevel@tonic-gate 
1609*0Sstevel@tonic-gate 	/* Find devmap handle */
1610*0Sstevel@tonic-gate 	if ((dhp = devmap_find_handle(dhp_head, addr)) == NULL)
1611*0Sstevel@tonic-gate 		return (FC_NOMAP);
1612*0Sstevel@tonic-gate 
1613*0Sstevel@tonic-gate 	/*
1614*0Sstevel@tonic-gate 	 * The seg_dev driver does not implement copy-on-write,
1615*0Sstevel@tonic-gate 	 * and always loads translations with maximal allowed permissions
1616*0Sstevel@tonic-gate 	 * but we got an fault trying to access the device.
1617*0Sstevel@tonic-gate 	 * Servicing the fault is not going to result in any better result
1618*0Sstevel@tonic-gate 	 * RFE: If we want devmap_access callbacks to be involved in F_PROT
1619*0Sstevel@tonic-gate 	 *	faults, then the code below is written for that
1620*0Sstevel@tonic-gate 	 *	Pending resolution of the following:
1621*0Sstevel@tonic-gate 	 *	- determine if the F_INVAL/F_SOFTLOCK syncing
1622*0Sstevel@tonic-gate 	 *	is needed for F_PROT also or not. The code below assumes it does
1623*0Sstevel@tonic-gate 	 *	- If driver sees F_PROT and calls devmap_load with same type,
1624*0Sstevel@tonic-gate 	 *	then segdev_faultpages will fail with FC_PROT anyway, need to
1625*0Sstevel@tonic-gate 	 *	change that so calls from devmap_load to segdev_faultpages for
1626*0Sstevel@tonic-gate 	 *	F_PROT type are retagged to F_INVAL.
1627*0Sstevel@tonic-gate 	 * RFE: Today we dont have drivers that use devmap and want to handle
1628*0Sstevel@tonic-gate 	 *	F_PROT calls. The code in segdev_fault* is written to allow
1629*0Sstevel@tonic-gate 	 *	this case but is not tested. A driver that needs this capability
1630*0Sstevel@tonic-gate 	 *	should be able to remove the short-circuit case; resolve the
1631*0Sstevel@tonic-gate 	 *	above issues and "should" work.
1632*0Sstevel@tonic-gate 	 */
1633*0Sstevel@tonic-gate 	if (type == F_PROT) {
1634*0Sstevel@tonic-gate 		return (FC_PROT);
1635*0Sstevel@tonic-gate 	}
1636*0Sstevel@tonic-gate 
1637*0Sstevel@tonic-gate 	/*
1638*0Sstevel@tonic-gate 	 * Loop through dhp list calling devmap_access or segdev_faultpages for
1639*0Sstevel@tonic-gate 	 * each devmap handle.
1640*0Sstevel@tonic-gate 	 * drivers which implement devmap_access can interpose on faults and do
1641*0Sstevel@tonic-gate 	 * device-appropriate special actions before calling devmap_load.
1642*0Sstevel@tonic-gate 	 */
1643*0Sstevel@tonic-gate 
1644*0Sstevel@tonic-gate 	/*
1645*0Sstevel@tonic-gate 	 * Unfortunately, this simple loop has turned out to expose a variety
1646*0Sstevel@tonic-gate 	 * of complex problems which results in the following convoluted code.
1647*0Sstevel@tonic-gate 	 *
1648*0Sstevel@tonic-gate 	 * First, a desire to handle a serialization of F_SOFTLOCK calls
1649*0Sstevel@tonic-gate 	 * to the driver within the framework.
1650*0Sstevel@tonic-gate 	 *	This results in a dh_softlock structure that is on a per device
1651*0Sstevel@tonic-gate 	 *	(or device instance) basis and serializes devmap_access calls.
1652*0Sstevel@tonic-gate 	 *	Ideally we would need to do this for underlying
1653*0Sstevel@tonic-gate 	 *	memory/device regions that are being faulted on
1654*0Sstevel@tonic-gate 	 *	but that is hard to identify and with REMAP, harder
1655*0Sstevel@tonic-gate 	 * Second, a desire to serialize F_INVAL(and F_PROT) calls w.r.t.
1656*0Sstevel@tonic-gate 	 * 	to F_SOFTLOCK calls to the driver.
1657*0Sstevel@tonic-gate 	 * These serializations are to simplify the driver programmer model.
1658*0Sstevel@tonic-gate 	 * To support these two features, the code first goes through the
1659*0Sstevel@tonic-gate 	 *	devmap handles and counts the pages (slpage) that are covered
1660*0Sstevel@tonic-gate 	 *	by devmap_access callbacks.
1661*0Sstevel@tonic-gate 	 * This part ends with a devmap_softlock_enter call
1662*0Sstevel@tonic-gate 	 *	which allows only one F_SOFTLOCK active on a device instance,
1663*0Sstevel@tonic-gate 	 *	but multiple F_INVAL/F_PROTs can be active except when a
1664*0Sstevel@tonic-gate 	 *	F_SOFTLOCK is active
1665*0Sstevel@tonic-gate 	 *
1666*0Sstevel@tonic-gate 	 * Next, we dont short-circuit the fault code upfront to call
1667*0Sstevel@tonic-gate 	 *	segdev_softunlock for F_SOFTUNLOCK, because we must use
1668*0Sstevel@tonic-gate 	 *	the same length when we softlock and softunlock.
1669*0Sstevel@tonic-gate 	 *
1670*0Sstevel@tonic-gate 	 *	-Hat layers may not support softunlocking lengths less than the
1671*0Sstevel@tonic-gate 	 *	original length when there is large page support.
1672*0Sstevel@tonic-gate 	 *	-kpmem locking is dependent on keeping the lengths same.
1673*0Sstevel@tonic-gate 	 *	-if drivers handled F_SOFTLOCK, they probably also expect to
1674*0Sstevel@tonic-gate 	 *		see an F_SOFTUNLOCK of the same length
1675*0Sstevel@tonic-gate 	 *	Hence, if extending lengths during softlock,
1676*0Sstevel@tonic-gate 	 *	softunlock has to make the same adjustments and goes through
1677*0Sstevel@tonic-gate 	 *	the same loop calling segdev_faultpages/segdev_softunlock
1678*0Sstevel@tonic-gate 	 *	But some of the synchronization and error handling is different
1679*0Sstevel@tonic-gate 	 */
1680*0Sstevel@tonic-gate 
1681*0Sstevel@tonic-gate 	if (type != F_SOFTUNLOCK) {
1682*0Sstevel@tonic-gate 		devmap_handle_t *dhpp = dhp;
1683*0Sstevel@tonic-gate 		size_t slen = len;
1684*0Sstevel@tonic-gate 
1685*0Sstevel@tonic-gate 		/*
1686*0Sstevel@tonic-gate 		 * Calculate count of pages that are :
1687*0Sstevel@tonic-gate 		 * a) within the (potentially extended) fault region
1688*0Sstevel@tonic-gate 		 * b) AND covered by devmap handle with devmap_access
1689*0Sstevel@tonic-gate 		 */
1690*0Sstevel@tonic-gate 		off = (ulong_t)(addr - dhpp->dh_uvaddr);
1691*0Sstevel@tonic-gate 		while (slen != 0) {
1692*0Sstevel@tonic-gate 			size_t mlen;
1693*0Sstevel@tonic-gate 
1694*0Sstevel@tonic-gate 			/*
1695*0Sstevel@tonic-gate 			 * Softlocking on a region that allows remap is
1696*0Sstevel@tonic-gate 			 * unsupported due to unresolved locking issues
1697*0Sstevel@tonic-gate 			 * XXX: unclear what these are?
1698*0Sstevel@tonic-gate 			 *	One potential is that if there is a pending
1699*0Sstevel@tonic-gate 			 *	softlock, then a remap should not be allowed
1700*0Sstevel@tonic-gate 			 *	until the unlock is done. This is easily
1701*0Sstevel@tonic-gate 			 *	fixed by returning error in devmap*remap on
1702*0Sstevel@tonic-gate 			 *	checking the dh->dh_softlock->softlocked value
1703*0Sstevel@tonic-gate 			 */
1704*0Sstevel@tonic-gate 			if ((type == F_SOFTLOCK) &&
1705*0Sstevel@tonic-gate 			    (dhpp->dh_flags & DEVMAP_ALLOW_REMAP)) {
1706*0Sstevel@tonic-gate 				return (FC_NOSUPPORT);
1707*0Sstevel@tonic-gate 			}
1708*0Sstevel@tonic-gate 
1709*0Sstevel@tonic-gate 			mlen = MIN(slen, (dhpp->dh_len - off));
1710*0Sstevel@tonic-gate 			if (dhpp->dh_callbackops.devmap_access) {
1711*0Sstevel@tonic-gate 				size_t llen;
1712*0Sstevel@tonic-gate 				caddr_t laddr;
1713*0Sstevel@tonic-gate 				/*
1714*0Sstevel@tonic-gate 				 * use extended length for large page mappings
1715*0Sstevel@tonic-gate 				 */
1716*0Sstevel@tonic-gate 				HOLD_DHP_LOCK(dhpp);
1717*0Sstevel@tonic-gate 				if ((sdp->pageprot == 0) &&
1718*0Sstevel@tonic-gate 				    (dhpp->dh_flags & DEVMAP_FLAG_LARGE)) {
1719*0Sstevel@tonic-gate 					devmap_get_large_pgsize(dhpp,
1720*0Sstevel@tonic-gate 					    mlen, maddr, &llen, &laddr);
1721*0Sstevel@tonic-gate 				} else {
1722*0Sstevel@tonic-gate 					llen = mlen;
1723*0Sstevel@tonic-gate 				}
1724*0Sstevel@tonic-gate 				RELE_DHP_LOCK(dhpp);
1725*0Sstevel@tonic-gate 
1726*0Sstevel@tonic-gate 				slpage += btopr(llen);
1727*0Sstevel@tonic-gate 				slock = dhpp->dh_softlock;
1728*0Sstevel@tonic-gate 			}
1729*0Sstevel@tonic-gate 			maddr += mlen;
1730*0Sstevel@tonic-gate 			ASSERT(slen >= mlen);
1731*0Sstevel@tonic-gate 			slen -= mlen;
1732*0Sstevel@tonic-gate 			dhpp = dhpp->dh_next;
1733*0Sstevel@tonic-gate 			off = 0;
1734*0Sstevel@tonic-gate 		}
1735*0Sstevel@tonic-gate 		/*
1736*0Sstevel@tonic-gate 		 * synchonize with other faulting threads and wait till safe
1737*0Sstevel@tonic-gate 		 * devmap_softlock_enter might return due to signal in cv_wait
1738*0Sstevel@tonic-gate 		 *
1739*0Sstevel@tonic-gate 		 * devmap_softlock_enter has to be called outside of while loop
1740*0Sstevel@tonic-gate 		 * to prevent a deadlock if len spans over multiple dhps.
1741*0Sstevel@tonic-gate 		 * dh_softlock is based on device instance and if multiple dhps
1742*0Sstevel@tonic-gate 		 * use the same device instance, the second dhp's LOCK call
1743*0Sstevel@tonic-gate 		 * will hang waiting on the first to complete.
1744*0Sstevel@tonic-gate 		 * devmap_setup verifies that slocks in a dhp_chain are same.
1745*0Sstevel@tonic-gate 		 * RFE: this deadlock only hold true for F_SOFTLOCK. For
1746*0Sstevel@tonic-gate 		 * 	F_INVAL/F_PROT, since we now allow multiple in parallel,
1747*0Sstevel@tonic-gate 		 *	we could have done the softlock_enter inside the loop
1748*0Sstevel@tonic-gate 		 *	and supported multi-dhp mappings with dissimilar devices
1749*0Sstevel@tonic-gate 		 */
1750*0Sstevel@tonic-gate 		if (err = devmap_softlock_enter(slock, slpage, type))
1751*0Sstevel@tonic-gate 			return (FC_MAKE_ERR(err));
1752*0Sstevel@tonic-gate 	}
1753*0Sstevel@tonic-gate 
1754*0Sstevel@tonic-gate 	/* reset 'maddr' to the start addr of the range of fault. */
1755*0Sstevel@tonic-gate 	maddr = addr;
1756*0Sstevel@tonic-gate 
1757*0Sstevel@tonic-gate 	/* calculate the offset corresponds to 'addr' in the first dhp. */
1758*0Sstevel@tonic-gate 	off = (ulong_t)(addr - dhp->dh_uvaddr);
1759*0Sstevel@tonic-gate 
1760*0Sstevel@tonic-gate 	/*
1761*0Sstevel@tonic-gate 	 * The fault length may span over multiple dhps.
1762*0Sstevel@tonic-gate 	 * Loop until the total length is satisfied.
1763*0Sstevel@tonic-gate 	 */
1764*0Sstevel@tonic-gate 	while (len != 0) {
1765*0Sstevel@tonic-gate 		size_t llen;
1766*0Sstevel@tonic-gate 		size_t mlen;
1767*0Sstevel@tonic-gate 		caddr_t laddr;
1768*0Sstevel@tonic-gate 
1769*0Sstevel@tonic-gate 		/*
1770*0Sstevel@tonic-gate 		 * mlen is the smaller of 'len' and the length
1771*0Sstevel@tonic-gate 		 * from addr to the end of mapping defined by dhp.
1772*0Sstevel@tonic-gate 		 */
1773*0Sstevel@tonic-gate 		mlen = MIN(len, (dhp->dh_len - off));
1774*0Sstevel@tonic-gate 
1775*0Sstevel@tonic-gate 		HOLD_DHP_LOCK(dhp);
1776*0Sstevel@tonic-gate 		/*
1777*0Sstevel@tonic-gate 		 * Pass the extended length and address to devmap_access
1778*0Sstevel@tonic-gate 		 * if large pagesize is used for loading address translations.
1779*0Sstevel@tonic-gate 		 */
1780*0Sstevel@tonic-gate 		if ((sdp->pageprot == 0) &&
1781*0Sstevel@tonic-gate 		    (dhp->dh_flags & DEVMAP_FLAG_LARGE)) {
1782*0Sstevel@tonic-gate 			devmap_get_large_pgsize(dhp, mlen, maddr,
1783*0Sstevel@tonic-gate 				&llen, &laddr);
1784*0Sstevel@tonic-gate 			ASSERT(maddr == addr || laddr == maddr);
1785*0Sstevel@tonic-gate 		} else {
1786*0Sstevel@tonic-gate 			llen = mlen;
1787*0Sstevel@tonic-gate 			laddr = maddr;
1788*0Sstevel@tonic-gate 		}
1789*0Sstevel@tonic-gate 
1790*0Sstevel@tonic-gate 		if (dhp->dh_callbackops.devmap_access != NULL) {
1791*0Sstevel@tonic-gate 			offset_t aoff;
1792*0Sstevel@tonic-gate 
1793*0Sstevel@tonic-gate 			aoff = sdp->offset + (offset_t)(laddr - seg->s_base);
1794*0Sstevel@tonic-gate 
1795*0Sstevel@tonic-gate 			/*
1796*0Sstevel@tonic-gate 			 * call driver's devmap_access entry point which will
1797*0Sstevel@tonic-gate 			 * call devmap_load/contextmgmt to load the translations
1798*0Sstevel@tonic-gate 			 *
1799*0Sstevel@tonic-gate 			 * We drop the dhp_lock before calling access so
1800*0Sstevel@tonic-gate 			 * drivers can call devmap_*_remap within access
1801*0Sstevel@tonic-gate 			 */
1802*0Sstevel@tonic-gate 			RELE_DHP_LOCK(dhp);
1803*0Sstevel@tonic-gate 
1804*0Sstevel@tonic-gate 			err = (*dhp->dh_callbackops.devmap_access)(
1805*0Sstevel@tonic-gate 			    dhp, (void *)dhp->dh_pvtp, aoff, llen, type, rw);
1806*0Sstevel@tonic-gate 		} else {
1807*0Sstevel@tonic-gate 			/*
1808*0Sstevel@tonic-gate 			 * If no devmap_access entry point, then load mappings
1809*0Sstevel@tonic-gate 			 * hold dhp_lock across faultpages if REMAP
1810*0Sstevel@tonic-gate 			 */
1811*0Sstevel@tonic-gate 			err = segdev_faultpages(hat, seg, laddr, llen,
1812*0Sstevel@tonic-gate 			    type, rw, dhp);
1813*0Sstevel@tonic-gate 			err_is_faultcode = 1;
1814*0Sstevel@tonic-gate 			RELE_DHP_LOCK(dhp);
1815*0Sstevel@tonic-gate 		}
1816*0Sstevel@tonic-gate 
1817*0Sstevel@tonic-gate 		if (err) {
1818*0Sstevel@tonic-gate 			if ((type == F_SOFTLOCK) && (maddr > addr)) {
1819*0Sstevel@tonic-gate 				/*
1820*0Sstevel@tonic-gate 				 * If not first dhp, use
1821*0Sstevel@tonic-gate 				 * segdev_fault(F_SOFTUNLOCK) for prior dhps
1822*0Sstevel@tonic-gate 				 * While this is recursion, it is incorrect to
1823*0Sstevel@tonic-gate 				 * call just segdev_softunlock
1824*0Sstevel@tonic-gate 				 * if we are using either large pages
1825*0Sstevel@tonic-gate 				 * or devmap_access. It will be more right
1826*0Sstevel@tonic-gate 				 * to go through the same loop as above
1827*0Sstevel@tonic-gate 				 * rather than call segdev_softunlock directly
1828*0Sstevel@tonic-gate 				 * It will use the right lenghths as well as
1829*0Sstevel@tonic-gate 				 * call into the driver devmap_access routines.
1830*0Sstevel@tonic-gate 				 */
1831*0Sstevel@tonic-gate 				size_t done = (size_t)(maddr - addr);
1832*0Sstevel@tonic-gate 				(void) segdev_fault(hat, seg, addr, done,
1833*0Sstevel@tonic-gate 					F_SOFTUNLOCK, S_OTHER);
1834*0Sstevel@tonic-gate 				/*
1835*0Sstevel@tonic-gate 				 * reduce slpage by number of pages
1836*0Sstevel@tonic-gate 				 * released by segdev_softunlock
1837*0Sstevel@tonic-gate 				 */
1838*0Sstevel@tonic-gate 				ASSERT(slpage >= btopr(done));
1839*0Sstevel@tonic-gate 				devmap_softlock_exit(slock,
1840*0Sstevel@tonic-gate 					slpage - btopr(done), type);
1841*0Sstevel@tonic-gate 			} else {
1842*0Sstevel@tonic-gate 				devmap_softlock_exit(slock, slpage, type);
1843*0Sstevel@tonic-gate 			}
1844*0Sstevel@tonic-gate 
1845*0Sstevel@tonic-gate 
1846*0Sstevel@tonic-gate 			/*
1847*0Sstevel@tonic-gate 			 * Segdev_faultpages() already returns a faultcode,
1848*0Sstevel@tonic-gate 			 * hence, result from segdev_faultpages() should be
1849*0Sstevel@tonic-gate 			 * returned directly.
1850*0Sstevel@tonic-gate 			 */
1851*0Sstevel@tonic-gate 			if (err_is_faultcode)
1852*0Sstevel@tonic-gate 				return (err);
1853*0Sstevel@tonic-gate 			return (FC_MAKE_ERR(err));
1854*0Sstevel@tonic-gate 		}
1855*0Sstevel@tonic-gate 
1856*0Sstevel@tonic-gate 		maddr += mlen;
1857*0Sstevel@tonic-gate 		ASSERT(len >= mlen);
1858*0Sstevel@tonic-gate 		len -= mlen;
1859*0Sstevel@tonic-gate 		dhp = dhp->dh_next;
1860*0Sstevel@tonic-gate 		off = 0;
1861*0Sstevel@tonic-gate 
1862*0Sstevel@tonic-gate 		ASSERT(!dhp || len == 0 || maddr == dhp->dh_uvaddr);
1863*0Sstevel@tonic-gate 	}
1864*0Sstevel@tonic-gate 	/*
1865*0Sstevel@tonic-gate 	 * release the softlock count at end of fault
1866*0Sstevel@tonic-gate 	 * For F_SOFTLOCk this is done in the later F_SOFTUNLOCK
1867*0Sstevel@tonic-gate 	 */
1868*0Sstevel@tonic-gate 	if ((type == F_INVAL) || (type == F_PROT))
1869*0Sstevel@tonic-gate 		devmap_softlock_exit(slock, slpage, type);
1870*0Sstevel@tonic-gate 	return (0);
1871*0Sstevel@tonic-gate }
1872*0Sstevel@tonic-gate 
1873*0Sstevel@tonic-gate /*
1874*0Sstevel@tonic-gate  * segdev_faultpages
1875*0Sstevel@tonic-gate  *
1876*0Sstevel@tonic-gate  * Used to fault in seg_dev segment pages. Called by segdev_fault or devmap_load
1877*0Sstevel@tonic-gate  * This routine assumes that the callers makes sure that the fields
1878*0Sstevel@tonic-gate  * in dhp used below are not changed due to remap during this call.
1879*0Sstevel@tonic-gate  * Caller does HOLD_DHP_LOCK if neeed
1880*0Sstevel@tonic-gate  * This routine returns a faultcode_t as a return value for segdev_fault.
1881*0Sstevel@tonic-gate  */
1882*0Sstevel@tonic-gate static faultcode_t
1883*0Sstevel@tonic-gate segdev_faultpages(
1884*0Sstevel@tonic-gate 	struct hat *hat,		/* the hat */
1885*0Sstevel@tonic-gate 	struct seg *seg,		/* the seg_dev of interest */
1886*0Sstevel@tonic-gate 	caddr_t addr,			/* the address of the fault */
1887*0Sstevel@tonic-gate 	size_t len,			/* the length of the range */
1888*0Sstevel@tonic-gate 	enum fault_type type,		/* type of fault */
1889*0Sstevel@tonic-gate 	enum seg_rw rw,			/* type of access at fault */
1890*0Sstevel@tonic-gate 	devmap_handle_t *dhp)		/* devmap handle */
1891*0Sstevel@tonic-gate {
1892*0Sstevel@tonic-gate 	register struct segdev_data *sdp = (struct segdev_data *)seg->s_data;
1893*0Sstevel@tonic-gate 	register caddr_t a;
1894*0Sstevel@tonic-gate 	struct vpage *vpage;
1895*0Sstevel@tonic-gate 	struct ddi_umem_cookie *kpmem_cookie = NULL;
1896*0Sstevel@tonic-gate 	int err;
1897*0Sstevel@tonic-gate 
1898*0Sstevel@tonic-gate 	TRACE_4(TR_FAC_DEVMAP, TR_DEVMAP_FAULTPAGES,
1899*0Sstevel@tonic-gate 	    "segdev_faultpages: dhp=%p seg=%p addr=%p len=%lx",
1900*0Sstevel@tonic-gate 	    (void *)dhp, (void *)seg, (void *)addr, len);
1901*0Sstevel@tonic-gate 	DEBUGF(5, (CE_CONT, "segdev_faultpages: "
1902*0Sstevel@tonic-gate 	    "dhp %p seg %p addr %p len %lx\n",
1903*0Sstevel@tonic-gate 	    (void *)dhp, (void *)seg, (void *)addr, len));
1904*0Sstevel@tonic-gate 
1905*0Sstevel@tonic-gate 	/*
1906*0Sstevel@tonic-gate 	 * The seg_dev driver does not implement copy-on-write,
1907*0Sstevel@tonic-gate 	 * and always loads translations with maximal allowed permissions
1908*0Sstevel@tonic-gate 	 * but we got an fault trying to access the device.
1909*0Sstevel@tonic-gate 	 * Servicing the fault is not going to result in any better result
1910*0Sstevel@tonic-gate 	 * XXX: If we want to allow devmap_access to handle F_PROT calls,
1911*0Sstevel@tonic-gate 	 * This code should be removed and let the normal fault handling
1912*0Sstevel@tonic-gate 	 * take care of finding the error
1913*0Sstevel@tonic-gate 	 */
1914*0Sstevel@tonic-gate 	if (type == F_PROT) {
1915*0Sstevel@tonic-gate 		return (FC_PROT);
1916*0Sstevel@tonic-gate 	}
1917*0Sstevel@tonic-gate 
1918*0Sstevel@tonic-gate 	if (type == F_SOFTUNLOCK) {
1919*0Sstevel@tonic-gate 		segdev_softunlock(hat, seg, addr, len, rw);
1920*0Sstevel@tonic-gate 		return (0);
1921*0Sstevel@tonic-gate 	}
1922*0Sstevel@tonic-gate 
1923*0Sstevel@tonic-gate 	/*
1924*0Sstevel@tonic-gate 	 * For kernel pageable memory, fault/lock segkp pages
1925*0Sstevel@tonic-gate 	 * We hold this until the completion of this
1926*0Sstevel@tonic-gate 	 * fault (INVAL/PROT) or till unlock (SOFTLOCK).
1927*0Sstevel@tonic-gate 	 */
1928*0Sstevel@tonic-gate 	if ((dhp != NULL) && dhp_is_kpmem(dhp)) {
1929*0Sstevel@tonic-gate 		kpmem_cookie = (struct ddi_umem_cookie *)dhp->dh_cookie;
1930*0Sstevel@tonic-gate 		if (err = acquire_kpmem_lock(kpmem_cookie, btopr(len)))
1931*0Sstevel@tonic-gate 			return (err);
1932*0Sstevel@tonic-gate 	}
1933*0Sstevel@tonic-gate 
1934*0Sstevel@tonic-gate 	/*
1935*0Sstevel@tonic-gate 	 * If we have the same protections for the entire segment,
1936*0Sstevel@tonic-gate 	 * insure that the access being attempted is legitimate.
1937*0Sstevel@tonic-gate 	 */
1938*0Sstevel@tonic-gate 	mutex_enter(&sdp->lock);
1939*0Sstevel@tonic-gate 	if (sdp->pageprot == 0) {
1940*0Sstevel@tonic-gate 		uint_t protchk;
1941*0Sstevel@tonic-gate 
1942*0Sstevel@tonic-gate 		switch (rw) {
1943*0Sstevel@tonic-gate 		case S_READ:
1944*0Sstevel@tonic-gate 			protchk = PROT_READ;
1945*0Sstevel@tonic-gate 			break;
1946*0Sstevel@tonic-gate 		case S_WRITE:
1947*0Sstevel@tonic-gate 			protchk = PROT_WRITE;
1948*0Sstevel@tonic-gate 			break;
1949*0Sstevel@tonic-gate 		case S_EXEC:
1950*0Sstevel@tonic-gate 			protchk = PROT_EXEC;
1951*0Sstevel@tonic-gate 			break;
1952*0Sstevel@tonic-gate 		case S_OTHER:
1953*0Sstevel@tonic-gate 		default:
1954*0Sstevel@tonic-gate 			protchk = PROT_READ | PROT_WRITE | PROT_EXEC;
1955*0Sstevel@tonic-gate 			break;
1956*0Sstevel@tonic-gate 		}
1957*0Sstevel@tonic-gate 
1958*0Sstevel@tonic-gate 		if ((sdp->prot & protchk) == 0) {
1959*0Sstevel@tonic-gate 			mutex_exit(&sdp->lock);
1960*0Sstevel@tonic-gate 			/* undo kpmem locking */
1961*0Sstevel@tonic-gate 			if (kpmem_cookie != NULL) {
1962*0Sstevel@tonic-gate 				release_kpmem_lock(kpmem_cookie, btopr(len));
1963*0Sstevel@tonic-gate 			}
1964*0Sstevel@tonic-gate 			return (FC_PROT);	/* illegal access type */
1965*0Sstevel@tonic-gate 		}
1966*0Sstevel@tonic-gate 	}
1967*0Sstevel@tonic-gate 
1968*0Sstevel@tonic-gate 	/*
1969*0Sstevel@tonic-gate 	 * we do a single hat_devload for the range if
1970*0Sstevel@tonic-gate 	 *   - devmap framework (dhp is not NULL),
1971*0Sstevel@tonic-gate 	 *   - pageprot == 0, i.e., no per-page protection set and
1972*0Sstevel@tonic-gate 	 *   - is device pages, irrespective of whether we are using large pages
1973*0Sstevel@tonic-gate 	 */
1974*0Sstevel@tonic-gate 	if ((sdp->pageprot == 0) && (dhp != NULL) && dhp_is_devmem(dhp)) {
1975*0Sstevel@tonic-gate 		pfn_t pfnum;
1976*0Sstevel@tonic-gate 		uint_t hat_flags;
1977*0Sstevel@tonic-gate 
1978*0Sstevel@tonic-gate 		if (dhp->dh_flags & DEVMAP_MAPPING_INVALID) {
1979*0Sstevel@tonic-gate 			mutex_exit(&sdp->lock);
1980*0Sstevel@tonic-gate 			return (FC_NOMAP);
1981*0Sstevel@tonic-gate 		}
1982*0Sstevel@tonic-gate 
1983*0Sstevel@tonic-gate 		if (type == F_SOFTLOCK) {
1984*0Sstevel@tonic-gate 			mutex_enter(&freemem_lock);
1985*0Sstevel@tonic-gate 			sdp->softlockcnt += btopr(len);
1986*0Sstevel@tonic-gate 			mutex_exit(&freemem_lock);
1987*0Sstevel@tonic-gate 		}
1988*0Sstevel@tonic-gate 
1989*0Sstevel@tonic-gate 		hat_flags = ((type == F_SOFTLOCK) ? HAT_LOAD_LOCK : HAT_LOAD);
1990*0Sstevel@tonic-gate 		pfnum = dhp->dh_pfn + btop((uintptr_t)(addr - dhp->dh_uvaddr));
1991*0Sstevel@tonic-gate 		ASSERT(!pf_is_memory(pfnum));
1992*0Sstevel@tonic-gate 
1993*0Sstevel@tonic-gate 		hat_devload(hat, addr, len, pfnum, sdp->prot | dhp->dh_hat_attr,
1994*0Sstevel@tonic-gate 			hat_flags | sdp->hat_flags);
1995*0Sstevel@tonic-gate 		mutex_exit(&sdp->lock);
1996*0Sstevel@tonic-gate 		return (0);
1997*0Sstevel@tonic-gate 	}
1998*0Sstevel@tonic-gate 
1999*0Sstevel@tonic-gate 	/* Handle cases where we have to loop through fault handling per-page */
2000*0Sstevel@tonic-gate 
2001*0Sstevel@tonic-gate 	if (sdp->vpage == NULL)
2002*0Sstevel@tonic-gate 		vpage = NULL;
2003*0Sstevel@tonic-gate 	else
2004*0Sstevel@tonic-gate 		vpage = &sdp->vpage[seg_page(seg, addr)];
2005*0Sstevel@tonic-gate 
2006*0Sstevel@tonic-gate 	/* loop over the address range handling each fault */
2007*0Sstevel@tonic-gate 	for (a = addr; a < addr + len; a += PAGESIZE) {
2008*0Sstevel@tonic-gate 		if (err = segdev_faultpage(hat, seg, a, vpage, type, rw, dhp)) {
2009*0Sstevel@tonic-gate 			break;
2010*0Sstevel@tonic-gate 		}
2011*0Sstevel@tonic-gate 		if (vpage != NULL)
2012*0Sstevel@tonic-gate 			vpage++;
2013*0Sstevel@tonic-gate 	}
2014*0Sstevel@tonic-gate 	mutex_exit(&sdp->lock);
2015*0Sstevel@tonic-gate 	if (err && (type == F_SOFTLOCK)) { /* error handling for F_SOFTLOCK */
2016*0Sstevel@tonic-gate 		size_t done = (size_t)(a - addr); /* pages fault successfully */
2017*0Sstevel@tonic-gate 		if (done > 0) {
2018*0Sstevel@tonic-gate 			/* use softunlock for those pages */
2019*0Sstevel@tonic-gate 			segdev_softunlock(hat, seg, addr, done, S_OTHER);
2020*0Sstevel@tonic-gate 		}
2021*0Sstevel@tonic-gate 		if (kpmem_cookie != NULL) {
2022*0Sstevel@tonic-gate 			/* release kpmem lock for rest of pages */
2023*0Sstevel@tonic-gate 			ASSERT(len >= done);
2024*0Sstevel@tonic-gate 			release_kpmem_lock(kpmem_cookie, btopr(len - done));
2025*0Sstevel@tonic-gate 		}
2026*0Sstevel@tonic-gate 	} else if ((kpmem_cookie != NULL) && (type != F_SOFTLOCK)) {
2027*0Sstevel@tonic-gate 		/* for non-SOFTLOCK cases, release kpmem */
2028*0Sstevel@tonic-gate 		release_kpmem_lock(kpmem_cookie, btopr(len));
2029*0Sstevel@tonic-gate 	}
2030*0Sstevel@tonic-gate 	return (err);
2031*0Sstevel@tonic-gate }
2032*0Sstevel@tonic-gate 
2033*0Sstevel@tonic-gate /*
2034*0Sstevel@tonic-gate  * Asynchronous page fault.  We simply do nothing since this
2035*0Sstevel@tonic-gate  * entry point is not supposed to load up the translation.
2036*0Sstevel@tonic-gate  */
2037*0Sstevel@tonic-gate /*ARGSUSED*/
2038*0Sstevel@tonic-gate static faultcode_t
2039*0Sstevel@tonic-gate segdev_faulta(struct seg *seg, caddr_t addr)
2040*0Sstevel@tonic-gate {
2041*0Sstevel@tonic-gate 	TRACE_2(TR_FAC_DEVMAP, TR_DEVMAP_FAULTA,
2042*0Sstevel@tonic-gate 	    "segdev_faulta: seg=%p addr=%p", (void *)seg, (void *)addr);
2043*0Sstevel@tonic-gate 	ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as, &seg->s_as->a_lock));
2044*0Sstevel@tonic-gate 
2045*0Sstevel@tonic-gate 	return (0);
2046*0Sstevel@tonic-gate }
2047*0Sstevel@tonic-gate 
2048*0Sstevel@tonic-gate static int
2049*0Sstevel@tonic-gate segdev_setprot(struct seg *seg, caddr_t addr, size_t len, uint_t prot)
2050*0Sstevel@tonic-gate {
2051*0Sstevel@tonic-gate 	register struct segdev_data *sdp = (struct segdev_data *)seg->s_data;
2052*0Sstevel@tonic-gate 	register devmap_handle_t *dhp;
2053*0Sstevel@tonic-gate 	register struct vpage *vp, *evp;
2054*0Sstevel@tonic-gate 	devmap_handle_t *dhp_head = (devmap_handle_t *)sdp->devmap_data;
2055*0Sstevel@tonic-gate 	ulong_t off;
2056*0Sstevel@tonic-gate 	size_t mlen, sz;
2057*0Sstevel@tonic-gate 
2058*0Sstevel@tonic-gate 	TRACE_4(TR_FAC_DEVMAP, TR_DEVMAP_SETPROT,
2059*0Sstevel@tonic-gate 	    "segdev_setprot:start seg=%p addr=%p len=%lx prot=%x",
2060*0Sstevel@tonic-gate 	    (void *)seg, (void *)addr, len, prot);
2061*0Sstevel@tonic-gate 	ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as, &seg->s_as->a_lock));
2062*0Sstevel@tonic-gate 
2063*0Sstevel@tonic-gate 	if ((sz = sdp->softlockcnt) > 0 && dhp_head != NULL) {
2064*0Sstevel@tonic-gate 		/*
2065*0Sstevel@tonic-gate 		 * Fail the setprot if pages are SOFTLOCKed through this
2066*0Sstevel@tonic-gate 		 * mapping.
2067*0Sstevel@tonic-gate 		 * Softlockcnt is protected from change by the as read lock.
2068*0Sstevel@tonic-gate 		 */
2069*0Sstevel@tonic-gate 		TRACE_1(TR_FAC_DEVMAP, TR_DEVMAP_SETPROT_CK1,
2070*0Sstevel@tonic-gate 		    "segdev_setprot:error softlockcnt=%lx", sz);
2071*0Sstevel@tonic-gate 		DEBUGF(1, (CE_CONT, "segdev_setprot: softlockcnt %ld\n", sz));
2072*0Sstevel@tonic-gate 		return (EAGAIN);
2073*0Sstevel@tonic-gate 	}
2074*0Sstevel@tonic-gate 
2075*0Sstevel@tonic-gate 	if (dhp_head != NULL) {
2076*0Sstevel@tonic-gate 		if ((dhp = devmap_find_handle(dhp_head, addr)) == NULL)
2077*0Sstevel@tonic-gate 			return (EINVAL);
2078*0Sstevel@tonic-gate 
2079*0Sstevel@tonic-gate 		/*
2080*0Sstevel@tonic-gate 		 * check if violate maxprot.
2081*0Sstevel@tonic-gate 		 */
2082*0Sstevel@tonic-gate 		off = (ulong_t)(addr - dhp->dh_uvaddr);
2083*0Sstevel@tonic-gate 		mlen  = len;
2084*0Sstevel@tonic-gate 		while (dhp) {
2085*0Sstevel@tonic-gate 			if ((dhp->dh_maxprot & prot) != prot)
2086*0Sstevel@tonic-gate 				return (EACCES);	/* violated maxprot */
2087*0Sstevel@tonic-gate 
2088*0Sstevel@tonic-gate 			if (mlen > (dhp->dh_len - off)) {
2089*0Sstevel@tonic-gate 				mlen -= dhp->dh_len - off;
2090*0Sstevel@tonic-gate 				dhp = dhp->dh_next;
2091*0Sstevel@tonic-gate 				off = 0;
2092*0Sstevel@tonic-gate 			} else
2093*0Sstevel@tonic-gate 				break;
2094*0Sstevel@tonic-gate 		}
2095*0Sstevel@tonic-gate 	} else {
2096*0Sstevel@tonic-gate 		if ((sdp->maxprot & prot) != prot)
2097*0Sstevel@tonic-gate 			return (EACCES);
2098*0Sstevel@tonic-gate 	}
2099*0Sstevel@tonic-gate 
2100*0Sstevel@tonic-gate 	mutex_enter(&sdp->lock);
2101*0Sstevel@tonic-gate 	if (addr == seg->s_base && len == seg->s_size && sdp->pageprot == 0) {
2102*0Sstevel@tonic-gate 		if (sdp->prot == prot) {
2103*0Sstevel@tonic-gate 			mutex_exit(&sdp->lock);
2104*0Sstevel@tonic-gate 			return (0);			/* all done */
2105*0Sstevel@tonic-gate 		}
2106*0Sstevel@tonic-gate 		sdp->prot = (uchar_t)prot;
2107*0Sstevel@tonic-gate 	} else {
2108*0Sstevel@tonic-gate 		sdp->pageprot = 1;
2109*0Sstevel@tonic-gate 		if (sdp->vpage == NULL) {
2110*0Sstevel@tonic-gate 			/*
2111*0Sstevel@tonic-gate 			 * First time through setting per page permissions,
2112*0Sstevel@tonic-gate 			 * initialize all the vpage structures to prot
2113*0Sstevel@tonic-gate 			 */
2114*0Sstevel@tonic-gate 			sdp->vpage = kmem_zalloc(vpgtob(seg_pages(seg)),
2115*0Sstevel@tonic-gate 			    KM_SLEEP);
2116*0Sstevel@tonic-gate 			evp = &sdp->vpage[seg_pages(seg)];
2117*0Sstevel@tonic-gate 			for (vp = sdp->vpage; vp < evp; vp++)
2118*0Sstevel@tonic-gate 				VPP_SETPROT(vp, sdp->prot);
2119*0Sstevel@tonic-gate 		}
2120*0Sstevel@tonic-gate 		/*
2121*0Sstevel@tonic-gate 		 * Now go change the needed vpages protections.
2122*0Sstevel@tonic-gate 		 */
2123*0Sstevel@tonic-gate 		evp = &sdp->vpage[seg_page(seg, addr + len)];
2124*0Sstevel@tonic-gate 		for (vp = &sdp->vpage[seg_page(seg, addr)]; vp < evp; vp++)
2125*0Sstevel@tonic-gate 			VPP_SETPROT(vp, prot);
2126*0Sstevel@tonic-gate 	}
2127*0Sstevel@tonic-gate 	mutex_exit(&sdp->lock);
2128*0Sstevel@tonic-gate 
2129*0Sstevel@tonic-gate 	if (dhp_head != NULL) {
2130*0Sstevel@tonic-gate 		devmap_handle_t *tdhp;
2131*0Sstevel@tonic-gate 		/*
2132*0Sstevel@tonic-gate 		 * If large page size was used in hat_devload(),
2133*0Sstevel@tonic-gate 		 * the same page size must be used in hat_unload().
2134*0Sstevel@tonic-gate 		 */
2135*0Sstevel@tonic-gate 		dhp = tdhp = devmap_find_handle(dhp_head, addr);
2136*0Sstevel@tonic-gate 		while (tdhp != NULL) {
2137*0Sstevel@tonic-gate 			if (tdhp->dh_flags & DEVMAP_FLAG_LARGE) {
2138*0Sstevel@tonic-gate 				break;
2139*0Sstevel@tonic-gate 			}
2140*0Sstevel@tonic-gate 			tdhp = tdhp->dh_next;
2141*0Sstevel@tonic-gate 		}
2142*0Sstevel@tonic-gate 		if (tdhp) {
2143*0Sstevel@tonic-gate 			size_t slen = len;
2144*0Sstevel@tonic-gate 			size_t mlen;
2145*0Sstevel@tonic-gate 			size_t soff;
2146*0Sstevel@tonic-gate 
2147*0Sstevel@tonic-gate 			soff = (ulong_t)(addr - dhp->dh_uvaddr);
2148*0Sstevel@tonic-gate 			while (slen != 0) {
2149*0Sstevel@tonic-gate 				mlen = MIN(slen, (dhp->dh_len - soff));
2150*0Sstevel@tonic-gate 				hat_unload(seg->s_as->a_hat, dhp->dh_uvaddr,
2151*0Sstevel@tonic-gate 					dhp->dh_len, HAT_UNLOAD);
2152*0Sstevel@tonic-gate 				dhp = dhp->dh_next;
2153*0Sstevel@tonic-gate 				ASSERT(slen >= mlen);
2154*0Sstevel@tonic-gate 				slen -= mlen;
2155*0Sstevel@tonic-gate 				soff = 0;
2156*0Sstevel@tonic-gate 			}
2157*0Sstevel@tonic-gate 			return (0);
2158*0Sstevel@tonic-gate 		}
2159*0Sstevel@tonic-gate 	}
2160*0Sstevel@tonic-gate 
2161*0Sstevel@tonic-gate 	if ((prot & ~PROT_USER) == PROT_NONE) {
2162*0Sstevel@tonic-gate 		hat_unload(seg->s_as->a_hat, addr, len, HAT_UNLOAD);
2163*0Sstevel@tonic-gate 	} else {
2164*0Sstevel@tonic-gate 		/*
2165*0Sstevel@tonic-gate 		 * RFE: the segment should keep track of all attributes
2166*0Sstevel@tonic-gate 		 * allowing us to remove the deprecated hat_chgprot
2167*0Sstevel@tonic-gate 		 * and use hat_chgattr.
2168*0Sstevel@tonic-gate 		 */
2169*0Sstevel@tonic-gate 		hat_chgprot(seg->s_as->a_hat, addr, len, prot);
2170*0Sstevel@tonic-gate 	}
2171*0Sstevel@tonic-gate 
2172*0Sstevel@tonic-gate 	return (0);
2173*0Sstevel@tonic-gate }
2174*0Sstevel@tonic-gate 
2175*0Sstevel@tonic-gate static int
2176*0Sstevel@tonic-gate segdev_checkprot(struct seg *seg, caddr_t addr, size_t len, uint_t prot)
2177*0Sstevel@tonic-gate {
2178*0Sstevel@tonic-gate 	struct segdev_data *sdp = (struct segdev_data *)seg->s_data;
2179*0Sstevel@tonic-gate 	struct vpage *vp, *evp;
2180*0Sstevel@tonic-gate 
2181*0Sstevel@tonic-gate 	TRACE_4(TR_FAC_DEVMAP, TR_DEVMAP_CHECKPROT,
2182*0Sstevel@tonic-gate 	    "segdev_checkprot:start seg=%p addr=%p len=%lx prot=%x",
2183*0Sstevel@tonic-gate 	    (void *)seg, (void *)addr, len, prot);
2184*0Sstevel@tonic-gate 	ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as, &seg->s_as->a_lock));
2185*0Sstevel@tonic-gate 
2186*0Sstevel@tonic-gate 	/*
2187*0Sstevel@tonic-gate 	 * If segment protection can be used, simply check against them
2188*0Sstevel@tonic-gate 	 */
2189*0Sstevel@tonic-gate 	mutex_enter(&sdp->lock);
2190*0Sstevel@tonic-gate 	if (sdp->pageprot == 0) {
2191*0Sstevel@tonic-gate 		register int err;
2192*0Sstevel@tonic-gate 
2193*0Sstevel@tonic-gate 		err = ((sdp->prot & prot) != prot) ? EACCES : 0;
2194*0Sstevel@tonic-gate 		mutex_exit(&sdp->lock);
2195*0Sstevel@tonic-gate 		return (err);
2196*0Sstevel@tonic-gate 	}
2197*0Sstevel@tonic-gate 
2198*0Sstevel@tonic-gate 	/*
2199*0Sstevel@tonic-gate 	 * Have to check down to the vpage level
2200*0Sstevel@tonic-gate 	 */
2201*0Sstevel@tonic-gate 	evp = &sdp->vpage[seg_page(seg, addr + len)];
2202*0Sstevel@tonic-gate 	for (vp = &sdp->vpage[seg_page(seg, addr)]; vp < evp; vp++) {
2203*0Sstevel@tonic-gate 		if ((VPP_PROT(vp) & prot) != prot) {
2204*0Sstevel@tonic-gate 			mutex_exit(&sdp->lock);
2205*0Sstevel@tonic-gate 			return (EACCES);
2206*0Sstevel@tonic-gate 		}
2207*0Sstevel@tonic-gate 	}
2208*0Sstevel@tonic-gate 	mutex_exit(&sdp->lock);
2209*0Sstevel@tonic-gate 	return (0);
2210*0Sstevel@tonic-gate }
2211*0Sstevel@tonic-gate 
2212*0Sstevel@tonic-gate static int
2213*0Sstevel@tonic-gate segdev_getprot(struct seg *seg, caddr_t addr, size_t len, uint_t *protv)
2214*0Sstevel@tonic-gate {
2215*0Sstevel@tonic-gate 	struct segdev_data *sdp = (struct segdev_data *)seg->s_data;
2216*0Sstevel@tonic-gate 	size_t pgno;
2217*0Sstevel@tonic-gate 
2218*0Sstevel@tonic-gate 	TRACE_4(TR_FAC_DEVMAP, TR_DEVMAP_GETPROT,
2219*0Sstevel@tonic-gate 	    "segdev_getprot:start seg=%p addr=%p len=%lx protv=%p",
2220*0Sstevel@tonic-gate 	    (void *)seg, (void *)addr, len, (void *)protv);
2221*0Sstevel@tonic-gate 	ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as, &seg->s_as->a_lock));
2222*0Sstevel@tonic-gate 
2223*0Sstevel@tonic-gate 	pgno = seg_page(seg, addr + len) - seg_page(seg, addr) + 1;
2224*0Sstevel@tonic-gate 	if (pgno != 0) {
2225*0Sstevel@tonic-gate 		mutex_enter(&sdp->lock);
2226*0Sstevel@tonic-gate 		if (sdp->pageprot == 0) {
2227*0Sstevel@tonic-gate 			do
2228*0Sstevel@tonic-gate 				protv[--pgno] = sdp->prot;
2229*0Sstevel@tonic-gate 			while (pgno != 0);
2230*0Sstevel@tonic-gate 		} else {
2231*0Sstevel@tonic-gate 			size_t pgoff = seg_page(seg, addr);
2232*0Sstevel@tonic-gate 
2233*0Sstevel@tonic-gate 			do {
2234*0Sstevel@tonic-gate 				pgno--;
2235*0Sstevel@tonic-gate 				protv[pgno] =
2236*0Sstevel@tonic-gate 					VPP_PROT(&sdp->vpage[pgno + pgoff]);
2237*0Sstevel@tonic-gate 			} while (pgno != 0);
2238*0Sstevel@tonic-gate 		}
2239*0Sstevel@tonic-gate 		mutex_exit(&sdp->lock);
2240*0Sstevel@tonic-gate 	}
2241*0Sstevel@tonic-gate 	return (0);
2242*0Sstevel@tonic-gate }
2243*0Sstevel@tonic-gate 
2244*0Sstevel@tonic-gate static u_offset_t
2245*0Sstevel@tonic-gate segdev_getoffset(register struct seg *seg, caddr_t addr)
2246*0Sstevel@tonic-gate {
2247*0Sstevel@tonic-gate 	register struct segdev_data *sdp = (struct segdev_data *)seg->s_data;
2248*0Sstevel@tonic-gate 
2249*0Sstevel@tonic-gate 	TRACE_2(TR_FAC_DEVMAP, TR_DEVMAP_GETOFFSET,
2250*0Sstevel@tonic-gate 	    "segdev_getoffset:start seg=%p addr=%p", (void *)seg, (void *)addr);
2251*0Sstevel@tonic-gate 
2252*0Sstevel@tonic-gate 	ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as, &seg->s_as->a_lock));
2253*0Sstevel@tonic-gate 
2254*0Sstevel@tonic-gate 	return ((u_offset_t)sdp->offset + (addr - seg->s_base));
2255*0Sstevel@tonic-gate }
2256*0Sstevel@tonic-gate 
2257*0Sstevel@tonic-gate /*ARGSUSED*/
2258*0Sstevel@tonic-gate static int
2259*0Sstevel@tonic-gate segdev_gettype(register struct seg *seg, caddr_t addr)
2260*0Sstevel@tonic-gate {
2261*0Sstevel@tonic-gate 	register struct segdev_data *sdp = (struct segdev_data *)seg->s_data;
2262*0Sstevel@tonic-gate 
2263*0Sstevel@tonic-gate 	TRACE_2(TR_FAC_DEVMAP, TR_DEVMAP_GETTYPE,
2264*0Sstevel@tonic-gate 	    "segdev_gettype:start seg=%p addr=%p", (void *)seg, (void *)addr);
2265*0Sstevel@tonic-gate 
2266*0Sstevel@tonic-gate 	ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as, &seg->s_as->a_lock));
2267*0Sstevel@tonic-gate 
2268*0Sstevel@tonic-gate 	return (sdp->type);
2269*0Sstevel@tonic-gate }
2270*0Sstevel@tonic-gate 
2271*0Sstevel@tonic-gate 
2272*0Sstevel@tonic-gate /*ARGSUSED*/
2273*0Sstevel@tonic-gate static int
2274*0Sstevel@tonic-gate segdev_getvp(register struct seg *seg, caddr_t addr, struct vnode **vpp)
2275*0Sstevel@tonic-gate {
2276*0Sstevel@tonic-gate 	register struct segdev_data *sdp = (struct segdev_data *)seg->s_data;
2277*0Sstevel@tonic-gate 
2278*0Sstevel@tonic-gate 	TRACE_2(TR_FAC_DEVMAP, TR_DEVMAP_GETVP,
2279*0Sstevel@tonic-gate 	    "segdev_getvp:start seg=%p addr=%p", (void *)seg, (void *)addr);
2280*0Sstevel@tonic-gate 
2281*0Sstevel@tonic-gate 	ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as, &seg->s_as->a_lock));
2282*0Sstevel@tonic-gate 
2283*0Sstevel@tonic-gate 	/*
2284*0Sstevel@tonic-gate 	 * Note that this vp is the common_vp of the device, where the
2285*0Sstevel@tonic-gate 	 * pages are hung ..
2286*0Sstevel@tonic-gate 	 */
2287*0Sstevel@tonic-gate 	*vpp = VTOCVP(sdp->vp);
2288*0Sstevel@tonic-gate 
2289*0Sstevel@tonic-gate 	return (0);
2290*0Sstevel@tonic-gate }
2291*0Sstevel@tonic-gate 
2292*0Sstevel@tonic-gate static void
2293*0Sstevel@tonic-gate segdev_badop(void)
2294*0Sstevel@tonic-gate {
2295*0Sstevel@tonic-gate 	TRACE_0(TR_FAC_DEVMAP, TR_DEVMAP_SEGDEV_BADOP,
2296*0Sstevel@tonic-gate 		"segdev_badop:start");
2297*0Sstevel@tonic-gate 	panic("segdev_badop");
2298*0Sstevel@tonic-gate 	/*NOTREACHED*/
2299*0Sstevel@tonic-gate }
2300*0Sstevel@tonic-gate 
2301*0Sstevel@tonic-gate /*
2302*0Sstevel@tonic-gate  * segdev pages are not in the cache, and thus can't really be controlled.
2303*0Sstevel@tonic-gate  * Hence, syncs are simply always successful.
2304*0Sstevel@tonic-gate  */
2305*0Sstevel@tonic-gate /*ARGSUSED*/
2306*0Sstevel@tonic-gate static int
2307*0Sstevel@tonic-gate segdev_sync(struct seg *seg, caddr_t addr, size_t len, int attr, uint_t flags)
2308*0Sstevel@tonic-gate {
2309*0Sstevel@tonic-gate 	TRACE_0(TR_FAC_DEVMAP, TR_DEVMAP_SYNC, "segdev_sync:start");
2310*0Sstevel@tonic-gate 
2311*0Sstevel@tonic-gate 	ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as, &seg->s_as->a_lock));
2312*0Sstevel@tonic-gate 
2313*0Sstevel@tonic-gate 	return (0);
2314*0Sstevel@tonic-gate }
2315*0Sstevel@tonic-gate 
2316*0Sstevel@tonic-gate /*
2317*0Sstevel@tonic-gate  * segdev pages are always "in core".
2318*0Sstevel@tonic-gate  */
2319*0Sstevel@tonic-gate /*ARGSUSED*/
2320*0Sstevel@tonic-gate static size_t
2321*0Sstevel@tonic-gate segdev_incore(struct seg *seg, caddr_t addr, size_t len, char *vec)
2322*0Sstevel@tonic-gate {
2323*0Sstevel@tonic-gate 	size_t v = 0;
2324*0Sstevel@tonic-gate 
2325*0Sstevel@tonic-gate 	TRACE_0(TR_FAC_DEVMAP, TR_DEVMAP_INCORE, "segdev_incore:start");
2326*0Sstevel@tonic-gate 
2327*0Sstevel@tonic-gate 	ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as, &seg->s_as->a_lock));
2328*0Sstevel@tonic-gate 
2329*0Sstevel@tonic-gate 	for (len = (len + PAGEOFFSET) & PAGEMASK; len; len -= PAGESIZE,
2330*0Sstevel@tonic-gate 	    v += PAGESIZE)
2331*0Sstevel@tonic-gate 		*vec++ = 1;
2332*0Sstevel@tonic-gate 	return (v);
2333*0Sstevel@tonic-gate }
2334*0Sstevel@tonic-gate 
2335*0Sstevel@tonic-gate /*
2336*0Sstevel@tonic-gate  * segdev pages are not in the cache, and thus can't really be controlled.
2337*0Sstevel@tonic-gate  * Hence, locks are simply always successful.
2338*0Sstevel@tonic-gate  */
2339*0Sstevel@tonic-gate /*ARGSUSED*/
2340*0Sstevel@tonic-gate static int
2341*0Sstevel@tonic-gate segdev_lockop(struct seg *seg, caddr_t addr,
2342*0Sstevel@tonic-gate     size_t len, int attr, int op, ulong_t *lockmap, size_t pos)
2343*0Sstevel@tonic-gate {
2344*0Sstevel@tonic-gate 	TRACE_0(TR_FAC_DEVMAP, TR_DEVMAP_LOCKOP, "segdev_lockop:start");
2345*0Sstevel@tonic-gate 
2346*0Sstevel@tonic-gate 	ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as, &seg->s_as->a_lock));
2347*0Sstevel@tonic-gate 
2348*0Sstevel@tonic-gate 	return (0);
2349*0Sstevel@tonic-gate }
2350*0Sstevel@tonic-gate 
2351*0Sstevel@tonic-gate /*
2352*0Sstevel@tonic-gate  * segdev pages are not in the cache, and thus can't really be controlled.
2353*0Sstevel@tonic-gate  * Hence, advise is simply always successful.
2354*0Sstevel@tonic-gate  */
2355*0Sstevel@tonic-gate /*ARGSUSED*/
2356*0Sstevel@tonic-gate static int
2357*0Sstevel@tonic-gate segdev_advise(struct seg *seg, caddr_t addr, size_t len, uint_t behav)
2358*0Sstevel@tonic-gate {
2359*0Sstevel@tonic-gate 	TRACE_0(TR_FAC_DEVMAP, TR_DEVMAP_ADVISE, "segdev_advise:start");
2360*0Sstevel@tonic-gate 
2361*0Sstevel@tonic-gate 	ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as, &seg->s_as->a_lock));
2362*0Sstevel@tonic-gate 
2363*0Sstevel@tonic-gate 	return (0);
2364*0Sstevel@tonic-gate }
2365*0Sstevel@tonic-gate 
2366*0Sstevel@tonic-gate /*
2367*0Sstevel@tonic-gate  * segdev pages are not dumped, so we just return
2368*0Sstevel@tonic-gate  */
2369*0Sstevel@tonic-gate /*ARGSUSED*/
2370*0Sstevel@tonic-gate static void
2371*0Sstevel@tonic-gate segdev_dump(struct seg *seg)
2372*0Sstevel@tonic-gate {}
2373*0Sstevel@tonic-gate 
2374*0Sstevel@tonic-gate /*
2375*0Sstevel@tonic-gate  * ddi_segmap_setup:	Used by drivers who wish specify mapping attributes
2376*0Sstevel@tonic-gate  *			for a segment.	Called from a drivers segmap(9E)
2377*0Sstevel@tonic-gate  *			routine.
2378*0Sstevel@tonic-gate  */
2379*0Sstevel@tonic-gate /*ARGSUSED*/
2380*0Sstevel@tonic-gate int
2381*0Sstevel@tonic-gate ddi_segmap_setup(dev_t dev, off_t offset, struct as *as, caddr_t *addrp,
2382*0Sstevel@tonic-gate     off_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cred,
2383*0Sstevel@tonic-gate     ddi_device_acc_attr_t *accattrp, uint_t rnumber)
2384*0Sstevel@tonic-gate {
2385*0Sstevel@tonic-gate 	struct segdev_crargs dev_a;
2386*0Sstevel@tonic-gate 	int (*mapfunc)(dev_t dev, off_t off, int prot);
2387*0Sstevel@tonic-gate 	uint_t hat_attr;
2388*0Sstevel@tonic-gate 	pfn_t pfn;
2389*0Sstevel@tonic-gate 	int	error, i;
2390*0Sstevel@tonic-gate 
2391*0Sstevel@tonic-gate 	TRACE_0(TR_FAC_DEVMAP, TR_DEVMAP_SEGMAP_SETUP,
2392*0Sstevel@tonic-gate 	    "ddi_segmap_setup:start");
2393*0Sstevel@tonic-gate 
2394*0Sstevel@tonic-gate 	if ((mapfunc = devopsp[getmajor(dev)]->devo_cb_ops->cb_mmap) == nodev)
2395*0Sstevel@tonic-gate 		return (ENODEV);
2396*0Sstevel@tonic-gate 
2397*0Sstevel@tonic-gate 	/*
2398*0Sstevel@tonic-gate 	 * Character devices that support the d_mmap
2399*0Sstevel@tonic-gate 	 * interface can only be mmap'ed shared.
2400*0Sstevel@tonic-gate 	 */
2401*0Sstevel@tonic-gate 	if ((flags & MAP_TYPE) != MAP_SHARED)
2402*0Sstevel@tonic-gate 		return (EINVAL);
2403*0Sstevel@tonic-gate 
2404*0Sstevel@tonic-gate 	/*
2405*0Sstevel@tonic-gate 	 * Check that this region is indeed mappable on this platform.
2406*0Sstevel@tonic-gate 	 * Use the mapping function.
2407*0Sstevel@tonic-gate 	 */
2408*0Sstevel@tonic-gate 	if (ddi_device_mapping_check(dev, accattrp, rnumber, &hat_attr) == -1)
2409*0Sstevel@tonic-gate 		return (ENXIO);
2410*0Sstevel@tonic-gate 
2411*0Sstevel@tonic-gate 	/*
2412*0Sstevel@tonic-gate 	 * Check to ensure that the entire range is
2413*0Sstevel@tonic-gate 	 * legal and we are not trying to map in
2414*0Sstevel@tonic-gate 	 * more than the device will let us.
2415*0Sstevel@tonic-gate 	 */
2416*0Sstevel@tonic-gate 	for (i = 0; i < len; i += PAGESIZE) {
2417*0Sstevel@tonic-gate 		if (i == 0) {
2418*0Sstevel@tonic-gate 			/*
2419*0Sstevel@tonic-gate 			 * Save the pfn at offset here. This pfn will be
2420*0Sstevel@tonic-gate 			 * used later to get user address.
2421*0Sstevel@tonic-gate 			 */
2422*0Sstevel@tonic-gate 			if ((pfn = (pfn_t)cdev_mmap(mapfunc, dev, offset,
2423*0Sstevel@tonic-gate 				maxprot)) == PFN_INVALID)
2424*0Sstevel@tonic-gate 				return (ENXIO);
2425*0Sstevel@tonic-gate 		} else {
2426*0Sstevel@tonic-gate 			if (cdev_mmap(mapfunc, dev, offset + i, maxprot) ==
2427*0Sstevel@tonic-gate 				PFN_INVALID)
2428*0Sstevel@tonic-gate 				return (ENXIO);
2429*0Sstevel@tonic-gate 		}
2430*0Sstevel@tonic-gate 	}
2431*0Sstevel@tonic-gate 
2432*0Sstevel@tonic-gate 	as_rangelock(as);
2433*0Sstevel@tonic-gate 	if ((flags & MAP_FIXED) == 0) {
2434*0Sstevel@tonic-gate 		/*
2435*0Sstevel@tonic-gate 		 * Pick an address w/o worrying about
2436*0Sstevel@tonic-gate 		 * any vac alignment constraints.
2437*0Sstevel@tonic-gate 		 */
2438*0Sstevel@tonic-gate 		map_addr(addrp, len, ptob(pfn), 0, flags);
2439*0Sstevel@tonic-gate 		if (*addrp == NULL) {
2440*0Sstevel@tonic-gate 			as_rangeunlock(as);
2441*0Sstevel@tonic-gate 			return (ENOMEM);
2442*0Sstevel@tonic-gate 		}
2443*0Sstevel@tonic-gate 	} else {
2444*0Sstevel@tonic-gate 		/*
2445*0Sstevel@tonic-gate 		 * User-specified address; blow away any previous mappings.
2446*0Sstevel@tonic-gate 		 */
2447*0Sstevel@tonic-gate 		(void) as_unmap(as, *addrp, len);
2448*0Sstevel@tonic-gate 	}
2449*0Sstevel@tonic-gate 
2450*0Sstevel@tonic-gate 	dev_a.mapfunc = mapfunc;
2451*0Sstevel@tonic-gate 	dev_a.dev = dev;
2452*0Sstevel@tonic-gate 	dev_a.offset = (offset_t)offset;
2453*0Sstevel@tonic-gate 	dev_a.type = flags & MAP_TYPE;
2454*0Sstevel@tonic-gate 	dev_a.prot = (uchar_t)prot;
2455*0Sstevel@tonic-gate 	dev_a.maxprot = (uchar_t)maxprot;
2456*0Sstevel@tonic-gate 	dev_a.hat_attr = hat_attr;
2457*0Sstevel@tonic-gate 	dev_a.hat_flags = 0;
2458*0Sstevel@tonic-gate 	dev_a.devmap_data = NULL;
2459*0Sstevel@tonic-gate 
2460*0Sstevel@tonic-gate 	error = as_map(as, *addrp, len, segdev_create, &dev_a);
2461*0Sstevel@tonic-gate 	as_rangeunlock(as);
2462*0Sstevel@tonic-gate 	return (error);
2463*0Sstevel@tonic-gate 
2464*0Sstevel@tonic-gate }
2465*0Sstevel@tonic-gate 
2466*0Sstevel@tonic-gate /*ARGSUSED*/
2467*0Sstevel@tonic-gate static int
2468*0Sstevel@tonic-gate segdev_pagelock(struct seg *seg, caddr_t addr, size_t len,
2469*0Sstevel@tonic-gate     struct page ***ppp, enum lock_type type, enum seg_rw rw)
2470*0Sstevel@tonic-gate {
2471*0Sstevel@tonic-gate 	TRACE_0(TR_FAC_DEVMAP, TR_DEVMAP_PAGELOCK,
2472*0Sstevel@tonic-gate 	    "segdev_pagelock:start");
2473*0Sstevel@tonic-gate 	return (ENOTSUP);
2474*0Sstevel@tonic-gate }
2475*0Sstevel@tonic-gate 
2476*0Sstevel@tonic-gate /*ARGSUSED*/
2477*0Sstevel@tonic-gate static int
2478*0Sstevel@tonic-gate segdev_setpagesize(struct seg *seg, caddr_t addr, size_t len,
2479*0Sstevel@tonic-gate     uint_t szc)
2480*0Sstevel@tonic-gate {
2481*0Sstevel@tonic-gate 	return (ENOTSUP);
2482*0Sstevel@tonic-gate }
2483*0Sstevel@tonic-gate 
2484*0Sstevel@tonic-gate /*
2485*0Sstevel@tonic-gate  * devmap_device: Used by devmap framework to establish mapping
2486*0Sstevel@tonic-gate  *                called by devmap_seup(9F) during map setup time.
2487*0Sstevel@tonic-gate  */
2488*0Sstevel@tonic-gate /*ARGSUSED*/
2489*0Sstevel@tonic-gate static int
2490*0Sstevel@tonic-gate devmap_device(devmap_handle_t *dhp, struct as *as, caddr_t *addr,
2491*0Sstevel@tonic-gate     offset_t off, size_t len, uint_t flags)
2492*0Sstevel@tonic-gate {
2493*0Sstevel@tonic-gate 	devmap_handle_t *rdhp, *maxdhp;
2494*0Sstevel@tonic-gate 	struct segdev_crargs dev_a;
2495*0Sstevel@tonic-gate 	int	err;
2496*0Sstevel@tonic-gate 	uint_t maxprot = PROT_ALL;
2497*0Sstevel@tonic-gate 	offset_t offset = 0;
2498*0Sstevel@tonic-gate 	pfn_t pfn;
2499*0Sstevel@tonic-gate 	struct devmap_pmem_cookie *pcp;
2500*0Sstevel@tonic-gate 
2501*0Sstevel@tonic-gate 	TRACE_4(TR_FAC_DEVMAP, TR_DEVMAP_DEVICE,
2502*0Sstevel@tonic-gate 	    "devmap_device:start dhp=%p addr=%p off=%llx, len=%lx",
2503*0Sstevel@tonic-gate 	    (void *)dhp, (void *)addr, off, len);
2504*0Sstevel@tonic-gate 
2505*0Sstevel@tonic-gate 	DEBUGF(2, (CE_CONT, "devmap_device: dhp %p addr %p off %llx len %lx\n",
2506*0Sstevel@tonic-gate 	    (void *)dhp, (void *)addr, off, len));
2507*0Sstevel@tonic-gate 
2508*0Sstevel@tonic-gate 	as_rangelock(as);
2509*0Sstevel@tonic-gate 	if ((flags & MAP_FIXED) == 0) {
2510*0Sstevel@tonic-gate 		offset_t aligned_off;
2511*0Sstevel@tonic-gate 
2512*0Sstevel@tonic-gate 		rdhp = maxdhp = dhp;
2513*0Sstevel@tonic-gate 		while (rdhp != NULL) {
2514*0Sstevel@tonic-gate 			maxdhp = (maxdhp->dh_len > rdhp->dh_len) ?
2515*0Sstevel@tonic-gate 				maxdhp : rdhp;
2516*0Sstevel@tonic-gate 			rdhp = rdhp->dh_next;
2517*0Sstevel@tonic-gate 			maxprot |= dhp->dh_maxprot;
2518*0Sstevel@tonic-gate 		}
2519*0Sstevel@tonic-gate 		offset = maxdhp->dh_uoff - dhp->dh_uoff;
2520*0Sstevel@tonic-gate 
2521*0Sstevel@tonic-gate 		/*
2522*0Sstevel@tonic-gate 		 * Use the dhp that has the
2523*0Sstevel@tonic-gate 		 * largest len to get user address.
2524*0Sstevel@tonic-gate 		 */
2525*0Sstevel@tonic-gate 		/*
2526*0Sstevel@tonic-gate 		 * If MAPPING_INVALID, cannot use dh_pfn/dh_cvaddr,
2527*0Sstevel@tonic-gate 		 * use 0 which is as good as any other.
2528*0Sstevel@tonic-gate 		 */
2529*0Sstevel@tonic-gate 		if (maxdhp->dh_flags & DEVMAP_MAPPING_INVALID) {
2530*0Sstevel@tonic-gate 			aligned_off = (offset_t)0;
2531*0Sstevel@tonic-gate 		} else if (dhp_is_devmem(maxdhp)) {
2532*0Sstevel@tonic-gate 			aligned_off = (offset_t)ptob(maxdhp->dh_pfn) - offset;
2533*0Sstevel@tonic-gate 		} else if (dhp_is_pmem(maxdhp)) {
2534*0Sstevel@tonic-gate 			pcp = (struct devmap_pmem_cookie *)maxdhp->dh_pcookie;
2535*0Sstevel@tonic-gate 			pfn = page_pptonum(
2536*0Sstevel@tonic-gate 			    pcp->dp_pparray[btop(maxdhp->dh_roff)]);
2537*0Sstevel@tonic-gate 			aligned_off = (offset_t)ptob(pfn) - offset;
2538*0Sstevel@tonic-gate 		} else {
2539*0Sstevel@tonic-gate 			aligned_off = (offset_t)(uintptr_t)maxdhp->dh_cvaddr -
2540*0Sstevel@tonic-gate 			    offset;
2541*0Sstevel@tonic-gate 		}
2542*0Sstevel@tonic-gate 
2543*0Sstevel@tonic-gate 		/*
2544*0Sstevel@tonic-gate 		 * Pick an address aligned to dh_cookie.
2545*0Sstevel@tonic-gate 		 * for kernel memory/user memory, cookie is cvaddr.
2546*0Sstevel@tonic-gate 		 * for device memory, cookie is physical address.
2547*0Sstevel@tonic-gate 		 */
2548*0Sstevel@tonic-gate 		map_addr(addr, len, aligned_off, 1, flags);
2549*0Sstevel@tonic-gate 		if (*addr == NULL) {
2550*0Sstevel@tonic-gate 			as_rangeunlock(as);
2551*0Sstevel@tonic-gate 			return (ENOMEM);
2552*0Sstevel@tonic-gate 		}
2553*0Sstevel@tonic-gate 	} else {
2554*0Sstevel@tonic-gate 		/*
2555*0Sstevel@tonic-gate 		 * User-specified address; blow away any previous mappings.
2556*0Sstevel@tonic-gate 		 */
2557*0Sstevel@tonic-gate 		(void) as_unmap(as, *addr, len);
2558*0Sstevel@tonic-gate 	}
2559*0Sstevel@tonic-gate 
2560*0Sstevel@tonic-gate 	dev_a.mapfunc = NULL;
2561*0Sstevel@tonic-gate 	dev_a.dev = dhp->dh_dev;
2562*0Sstevel@tonic-gate 	dev_a.type = flags & MAP_TYPE;
2563*0Sstevel@tonic-gate 	dev_a.offset = off;
2564*0Sstevel@tonic-gate 	/*
2565*0Sstevel@tonic-gate 	 * sdp->maxprot has the least restrict protection of all dhps.
2566*0Sstevel@tonic-gate 	 */
2567*0Sstevel@tonic-gate 	dev_a.maxprot = maxprot;
2568*0Sstevel@tonic-gate 	dev_a.prot = dhp->dh_prot;
2569*0Sstevel@tonic-gate 	/*
2570*0Sstevel@tonic-gate 	 * devmap uses dhp->dh_hat_attr for hat.
2571*0Sstevel@tonic-gate 	 */
2572*0Sstevel@tonic-gate 	dev_a.hat_flags = 0;
2573*0Sstevel@tonic-gate 	dev_a.hat_attr = 0;
2574*0Sstevel@tonic-gate 	dev_a.devmap_data = (void *)dhp;
2575*0Sstevel@tonic-gate 
2576*0Sstevel@tonic-gate 	err = as_map(as, *addr, len, segdev_create, &dev_a);
2577*0Sstevel@tonic-gate 	as_rangeunlock(as);
2578*0Sstevel@tonic-gate 	return (err);
2579*0Sstevel@tonic-gate }
2580*0Sstevel@tonic-gate 
2581*0Sstevel@tonic-gate int
2582*0Sstevel@tonic-gate devmap_do_ctxmgt(devmap_cookie_t dhc, void *pvtp, offset_t off, size_t len,
2583*0Sstevel@tonic-gate     uint_t type, uint_t rw, int (*ctxmgt)(devmap_cookie_t, void *, offset_t,
2584*0Sstevel@tonic-gate     size_t, uint_t, uint_t))
2585*0Sstevel@tonic-gate {
2586*0Sstevel@tonic-gate 	register devmap_handle_t *dhp = (devmap_handle_t *)dhc;
2587*0Sstevel@tonic-gate 	struct devmap_ctx *devctx;
2588*0Sstevel@tonic-gate 	int do_timeout = 0;
2589*0Sstevel@tonic-gate 	int ret;
2590*0Sstevel@tonic-gate 
2591*0Sstevel@tonic-gate #ifdef lint
2592*0Sstevel@tonic-gate 	pvtp = pvtp;
2593*0Sstevel@tonic-gate #endif
2594*0Sstevel@tonic-gate 
2595*0Sstevel@tonic-gate 	TRACE_3(TR_FAC_DEVMAP, TR_DEVMAP_DO_CTXMGT,
2596*0Sstevel@tonic-gate 	    "devmap_do_ctxmgt:start dhp=%p off=%llx, len=%lx",
2597*0Sstevel@tonic-gate 	    (void *)dhp, off, len);
2598*0Sstevel@tonic-gate 	DEBUGF(7, (CE_CONT, "devmap_do_ctxmgt: dhp %p off %llx len %lx\n",
2599*0Sstevel@tonic-gate 	    (void *)dhp, off, len));
2600*0Sstevel@tonic-gate 
2601*0Sstevel@tonic-gate 	if (ctxmgt == NULL)
2602*0Sstevel@tonic-gate 		return (FC_HWERR);
2603*0Sstevel@tonic-gate 
2604*0Sstevel@tonic-gate 	devctx = dhp->dh_ctx;
2605*0Sstevel@tonic-gate 
2606*0Sstevel@tonic-gate 	/*
2607*0Sstevel@tonic-gate 	 * If we are on an MP system with more than one cpu running
2608*0Sstevel@tonic-gate 	 * and if a thread on some CPU already has the context, wait
2609*0Sstevel@tonic-gate 	 * for it to finish if there is a hysteresis timeout.
2610*0Sstevel@tonic-gate 	 *
2611*0Sstevel@tonic-gate 	 * We call cv_wait() instead of cv_wait_sig() because
2612*0Sstevel@tonic-gate 	 * it does not matter much if it returned due to a signal
2613*0Sstevel@tonic-gate 	 * or due to a cv_signal() or cv_broadcast().  In either event
2614*0Sstevel@tonic-gate 	 * we need to complete the mapping otherwise the processes
2615*0Sstevel@tonic-gate 	 * will die with a SEGV.
2616*0Sstevel@tonic-gate 	 */
2617*0Sstevel@tonic-gate 	if ((dhp->dh_timeout_length > 0) && (ncpus > 1)) {
2618*0Sstevel@tonic-gate 		TRACE_2(TR_FAC_DEVMAP, TR_DEVMAP_DO_CTXMGT_CK1,
2619*0Sstevel@tonic-gate 		    "devmap_do_ctxmgt:doing hysteresis, devctl %p dhp %p",
2620*0Sstevel@tonic-gate 		    devctx, dhp);
2621*0Sstevel@tonic-gate 		do_timeout = 1;
2622*0Sstevel@tonic-gate 		mutex_enter(&devctx->lock);
2623*0Sstevel@tonic-gate 		while (devctx->oncpu)
2624*0Sstevel@tonic-gate 			cv_wait(&devctx->cv, &devctx->lock);
2625*0Sstevel@tonic-gate 		devctx->oncpu = 1;
2626*0Sstevel@tonic-gate 		mutex_exit(&devctx->lock);
2627*0Sstevel@tonic-gate 	}
2628*0Sstevel@tonic-gate 
2629*0Sstevel@tonic-gate 	/*
2630*0Sstevel@tonic-gate 	 * Call the contextmgt callback so that the driver can handle
2631*0Sstevel@tonic-gate 	 * the fault.
2632*0Sstevel@tonic-gate 	 */
2633*0Sstevel@tonic-gate 	ret = (*ctxmgt)(dhp, dhp->dh_pvtp, off, len, type, rw);
2634*0Sstevel@tonic-gate 
2635*0Sstevel@tonic-gate 	/*
2636*0Sstevel@tonic-gate 	 * If devmap_access() returned -1, then there was a hardware
2637*0Sstevel@tonic-gate 	 * error so we need to convert the return value to something
2638*0Sstevel@tonic-gate 	 * that trap() will understand.  Otherwise, the return value
2639*0Sstevel@tonic-gate 	 * is already a fault code generated by devmap_unload()
2640*0Sstevel@tonic-gate 	 * or devmap_load().
2641*0Sstevel@tonic-gate 	 */
2642*0Sstevel@tonic-gate 	if (ret) {
2643*0Sstevel@tonic-gate 		TRACE_3(TR_FAC_DEVMAP, TR_DEVMAP_DO_CTXMGT_CK2,
2644*0Sstevel@tonic-gate 		    "devmap_do_ctxmgt: ret=%x dhp=%p devctx=%p",
2645*0Sstevel@tonic-gate 		    ret, dhp, devctx);
2646*0Sstevel@tonic-gate 		DEBUGF(1, (CE_CONT, "devmap_do_ctxmgt: ret %x dhp %p\n",
2647*0Sstevel@tonic-gate 		    ret, (void *)dhp));
2648*0Sstevel@tonic-gate 		if (devctx->oncpu) {
2649*0Sstevel@tonic-gate 			mutex_enter(&devctx->lock);
2650*0Sstevel@tonic-gate 			devctx->oncpu = 0;
2651*0Sstevel@tonic-gate 			cv_signal(&devctx->cv);
2652*0Sstevel@tonic-gate 			mutex_exit(&devctx->lock);
2653*0Sstevel@tonic-gate 		}
2654*0Sstevel@tonic-gate 		return (FC_HWERR);
2655*0Sstevel@tonic-gate 	}
2656*0Sstevel@tonic-gate 
2657*0Sstevel@tonic-gate 	/*
2658*0Sstevel@tonic-gate 	 * Setup the timeout if we need to
2659*0Sstevel@tonic-gate 	 */
2660*0Sstevel@tonic-gate 	if (do_timeout) {
2661*0Sstevel@tonic-gate 		mutex_enter(&devctx->lock);
2662*0Sstevel@tonic-gate 		if (dhp->dh_timeout_length > 0) {
2663*0Sstevel@tonic-gate 			TRACE_0(TR_FAC_DEVMAP, TR_DEVMAP_DO_CTXMGT_CK3,
2664*0Sstevel@tonic-gate 			    "devmap_do_ctxmgt:timeout set");
2665*0Sstevel@tonic-gate 			devctx->timeout = timeout(devmap_ctxto,
2666*0Sstevel@tonic-gate 			    devctx, dhp->dh_timeout_length);
2667*0Sstevel@tonic-gate 		} else {
2668*0Sstevel@tonic-gate 			/*
2669*0Sstevel@tonic-gate 			 * We don't want to wait so set oncpu to
2670*0Sstevel@tonic-gate 			 * 0 and wake up anyone waiting.
2671*0Sstevel@tonic-gate 			 */
2672*0Sstevel@tonic-gate 			TRACE_0(TR_FAC_DEVMAP, TR_DEVMAP_DO_CTXMGT_CK4,
2673*0Sstevel@tonic-gate 			    "devmap_do_ctxmgt:timeout not set");
2674*0Sstevel@tonic-gate 			devctx->oncpu = 0;
2675*0Sstevel@tonic-gate 			cv_signal(&devctx->cv);
2676*0Sstevel@tonic-gate 		}
2677*0Sstevel@tonic-gate 		mutex_exit(&devctx->lock);
2678*0Sstevel@tonic-gate 	}
2679*0Sstevel@tonic-gate 
2680*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
2681*0Sstevel@tonic-gate }
2682*0Sstevel@tonic-gate 
2683*0Sstevel@tonic-gate /*
2684*0Sstevel@tonic-gate  *                                       end of mapping
2685*0Sstevel@tonic-gate  *                    poff   fault_offset         |
2686*0Sstevel@tonic-gate  *            base     |        |                 |
2687*0Sstevel@tonic-gate  *              |      |        |                 |
2688*0Sstevel@tonic-gate  *              V      V        V                 V
2689*0Sstevel@tonic-gate  *  +-----------+---------------+-------+---------+-------+
2690*0Sstevel@tonic-gate  *              ^               ^       ^         ^
2691*0Sstevel@tonic-gate  *              |<--- offset--->|<-len->|         |
2692*0Sstevel@tonic-gate  *              |<--- dh_len(size of mapping) --->|
2693*0Sstevel@tonic-gate  *                     |<--  pg -->|
2694*0Sstevel@tonic-gate  *                              -->|rlen|<--
2695*0Sstevel@tonic-gate  */
2696*0Sstevel@tonic-gate static ulong_t
2697*0Sstevel@tonic-gate devmap_roundup(devmap_handle_t *dhp, ulong_t offset, size_t len,
2698*0Sstevel@tonic-gate     ulong_t *opfn, ulong_t *pagesize)
2699*0Sstevel@tonic-gate {
2700*0Sstevel@tonic-gate 	register int level;
2701*0Sstevel@tonic-gate 	ulong_t pg;
2702*0Sstevel@tonic-gate 	ulong_t poff;
2703*0Sstevel@tonic-gate 	ulong_t base;
2704*0Sstevel@tonic-gate 	caddr_t uvaddr;
2705*0Sstevel@tonic-gate 	long rlen;
2706*0Sstevel@tonic-gate 
2707*0Sstevel@tonic-gate 	TRACE_3(TR_FAC_DEVMAP, TR_DEVMAP_ROUNDUP,
2708*0Sstevel@tonic-gate 	    "devmap_roundup:start dhp=%p off=%lx len=%lx",
2709*0Sstevel@tonic-gate 	    (void *)dhp, offset, len);
2710*0Sstevel@tonic-gate 	DEBUGF(2, (CE_CONT, "devmap_roundup: dhp %p off %lx len %lx\n",
2711*0Sstevel@tonic-gate 	    (void *)dhp, offset, len));
2712*0Sstevel@tonic-gate 
2713*0Sstevel@tonic-gate 	/*
2714*0Sstevel@tonic-gate 	 * get the max. pagesize that is aligned within the range
2715*0Sstevel@tonic-gate 	 * <dh_pfn, dh_pfn+offset>.
2716*0Sstevel@tonic-gate 	 *
2717*0Sstevel@tonic-gate 	 * The calculations below use physical address to ddetermine
2718*0Sstevel@tonic-gate 	 * the page size to use. The same calculations can use the
2719*0Sstevel@tonic-gate 	 * virtual address to determine the page size.
2720*0Sstevel@tonic-gate 	 */
2721*0Sstevel@tonic-gate 	base = (ulong_t)ptob(dhp->dh_pfn);
2722*0Sstevel@tonic-gate 	for (level = dhp->dh_mmulevel; level >= 0; level--) {
2723*0Sstevel@tonic-gate 		pg = page_get_pagesize(level);
2724*0Sstevel@tonic-gate 		poff = ((base + offset) & ~(pg - 1));
2725*0Sstevel@tonic-gate 		uvaddr = dhp->dh_uvaddr + (poff - base);
2726*0Sstevel@tonic-gate 		if ((poff >= base) &&
2727*0Sstevel@tonic-gate 		    ((poff + pg) <= (base + dhp->dh_len)) &&
2728*0Sstevel@tonic-gate 		    VA_PA_ALIGNED((uintptr_t)uvaddr, poff, pg))
2729*0Sstevel@tonic-gate 			break;
2730*0Sstevel@tonic-gate 	}
2731*0Sstevel@tonic-gate 
2732*0Sstevel@tonic-gate 	TRACE_3(TR_FAC_DEVMAP, TR_DEVMAP_ROUNDUP_CK1,
2733*0Sstevel@tonic-gate 	    "devmap_roundup: base=%lx poff=%lx dhp=%p",
2734*0Sstevel@tonic-gate 	    base, poff, dhp);
2735*0Sstevel@tonic-gate 	DEBUGF(2, (CE_CONT, "devmap_roundup: base %lx poff %lx pfn %lx\n",
2736*0Sstevel@tonic-gate 	    base, poff, dhp->dh_pfn));
2737*0Sstevel@tonic-gate 
2738*0Sstevel@tonic-gate 	ASSERT(VA_PA_ALIGNED((uintptr_t)uvaddr, poff, pg));
2739*0Sstevel@tonic-gate 	ASSERT(level >= 0);
2740*0Sstevel@tonic-gate 
2741*0Sstevel@tonic-gate 	*pagesize = pg;
2742*0Sstevel@tonic-gate 	*opfn = dhp->dh_pfn + btop(poff - base);
2743*0Sstevel@tonic-gate 
2744*0Sstevel@tonic-gate 	rlen = len + offset - (poff - base + pg);
2745*0Sstevel@tonic-gate 
2746*0Sstevel@tonic-gate 	ASSERT(rlen < (long)len);
2747*0Sstevel@tonic-gate 
2748*0Sstevel@tonic-gate 	TRACE_5(TR_FAC_DEVMAP, TR_DEVMAP_ROUNDUP_CK2,
2749*0Sstevel@tonic-gate 	    "devmap_roundup:ret dhp=%p level=%x rlen=%lx psiz=%p opfn=%p",
2750*0Sstevel@tonic-gate 	    (void *)dhp, level, rlen, pagesize, opfn);
2751*0Sstevel@tonic-gate 	DEBUGF(1, (CE_CONT, "devmap_roundup: dhp %p "
2752*0Sstevel@tonic-gate 	    "level %x rlen %lx psize %lx opfn %lx\n",
2753*0Sstevel@tonic-gate 	    (void *)dhp, level, rlen, *pagesize, *opfn));
2754*0Sstevel@tonic-gate 
2755*0Sstevel@tonic-gate 	return ((ulong_t)((rlen > 0) ? rlen : 0));
2756*0Sstevel@tonic-gate }
2757*0Sstevel@tonic-gate 
2758*0Sstevel@tonic-gate /*
2759*0Sstevel@tonic-gate  * find the dhp that contains addr.
2760*0Sstevel@tonic-gate  */
2761*0Sstevel@tonic-gate static devmap_handle_t *
2762*0Sstevel@tonic-gate devmap_find_handle(devmap_handle_t *dhp_head, caddr_t addr)
2763*0Sstevel@tonic-gate {
2764*0Sstevel@tonic-gate 	devmap_handle_t *dhp;
2765*0Sstevel@tonic-gate 
2766*0Sstevel@tonic-gate 	TRACE_0(TR_FAC_DEVMAP, TR_DEVMAP_FIND_HANDLE,
2767*0Sstevel@tonic-gate 	    "devmap_find_handle:start");
2768*0Sstevel@tonic-gate 
2769*0Sstevel@tonic-gate 	dhp = dhp_head;
2770*0Sstevel@tonic-gate 	while (dhp) {
2771*0Sstevel@tonic-gate 		if (addr >= dhp->dh_uvaddr &&
2772*0Sstevel@tonic-gate 		    addr < (dhp->dh_uvaddr + dhp->dh_len))
2773*0Sstevel@tonic-gate 			return (dhp);
2774*0Sstevel@tonic-gate 		dhp = dhp->dh_next;
2775*0Sstevel@tonic-gate 	}
2776*0Sstevel@tonic-gate 
2777*0Sstevel@tonic-gate 	return ((devmap_handle_t *)NULL);
2778*0Sstevel@tonic-gate }
2779*0Sstevel@tonic-gate 
2780*0Sstevel@tonic-gate /*
2781*0Sstevel@tonic-gate  * devmap_unload:
2782*0Sstevel@tonic-gate  *			Marks a segdev segment or pages if offset->offset+len
2783*0Sstevel@tonic-gate  *			is not the entire segment as intercept and unloads the
2784*0Sstevel@tonic-gate  *			pages in the range offset -> offset+len.
2785*0Sstevel@tonic-gate  */
2786*0Sstevel@tonic-gate int
2787*0Sstevel@tonic-gate devmap_unload(devmap_cookie_t dhc, offset_t offset, size_t len)
2788*0Sstevel@tonic-gate {
2789*0Sstevel@tonic-gate 	register devmap_handle_t *dhp = (devmap_handle_t *)dhc;
2790*0Sstevel@tonic-gate 	caddr_t	addr;
2791*0Sstevel@tonic-gate 	ulong_t	size;
2792*0Sstevel@tonic-gate 	ssize_t	soff;
2793*0Sstevel@tonic-gate 
2794*0Sstevel@tonic-gate 	TRACE_3(TR_FAC_DEVMAP, TR_DEVMAP_UNLOAD,
2795*0Sstevel@tonic-gate 	    "devmap_unload:start dhp=%p offset=%llx len=%lx",
2796*0Sstevel@tonic-gate 	    (void *)dhp, offset, len);
2797*0Sstevel@tonic-gate 	DEBUGF(7, (CE_CONT, "devmap_unload: dhp %p offset %llx len %lx\n",
2798*0Sstevel@tonic-gate 	    (void *)dhp, offset, len));
2799*0Sstevel@tonic-gate 
2800*0Sstevel@tonic-gate 	soff = (ssize_t)(offset - dhp->dh_uoff);
2801*0Sstevel@tonic-gate 	soff = round_down_p2(soff, PAGESIZE);
2802*0Sstevel@tonic-gate 	if (soff < 0 || soff >= dhp->dh_len)
2803*0Sstevel@tonic-gate 		return (FC_MAKE_ERR(EINVAL));
2804*0Sstevel@tonic-gate 
2805*0Sstevel@tonic-gate 	/*
2806*0Sstevel@tonic-gate 	 * Address and size must be page aligned.  Len is set to the
2807*0Sstevel@tonic-gate 	 * number of bytes in the number of pages that are required to
2808*0Sstevel@tonic-gate 	 * support len.  Offset is set to the byte offset of the first byte
2809*0Sstevel@tonic-gate 	 * of the page that contains offset.
2810*0Sstevel@tonic-gate 	 */
2811*0Sstevel@tonic-gate 	len = round_up_p2(len, PAGESIZE);
2812*0Sstevel@tonic-gate 
2813*0Sstevel@tonic-gate 	/*
2814*0Sstevel@tonic-gate 	 * If len is == 0, then calculate the size by getting
2815*0Sstevel@tonic-gate 	 * the number of bytes from offset to the end of the segment.
2816*0Sstevel@tonic-gate 	 */
2817*0Sstevel@tonic-gate 	if (len == 0)
2818*0Sstevel@tonic-gate 		size = dhp->dh_len - soff;
2819*0Sstevel@tonic-gate 	else {
2820*0Sstevel@tonic-gate 		size = len;
2821*0Sstevel@tonic-gate 		if ((soff + size) > dhp->dh_len)
2822*0Sstevel@tonic-gate 			return (FC_MAKE_ERR(EINVAL));
2823*0Sstevel@tonic-gate 	}
2824*0Sstevel@tonic-gate 
2825*0Sstevel@tonic-gate 	/*
2826*0Sstevel@tonic-gate 	 * The address is offset bytes from the base address of
2827*0Sstevel@tonic-gate 	 * the dhp.
2828*0Sstevel@tonic-gate 	 */
2829*0Sstevel@tonic-gate 	addr = (caddr_t)(soff + dhp->dh_uvaddr);
2830*0Sstevel@tonic-gate 
2831*0Sstevel@tonic-gate 	/*
2832*0Sstevel@tonic-gate 	 * If large page size was used in hat_devload(),
2833*0Sstevel@tonic-gate 	 * the same page size must be used in hat_unload().
2834*0Sstevel@tonic-gate 	 */
2835*0Sstevel@tonic-gate 	if (dhp->dh_flags & DEVMAP_FLAG_LARGE) {
2836*0Sstevel@tonic-gate 		hat_unload(dhp->dh_seg->s_as->a_hat, dhp->dh_uvaddr,
2837*0Sstevel@tonic-gate 			dhp->dh_len, HAT_UNLOAD|HAT_UNLOAD_OTHER);
2838*0Sstevel@tonic-gate 	} else {
2839*0Sstevel@tonic-gate 		hat_unload(dhp->dh_seg->s_as->a_hat,  addr, size,
2840*0Sstevel@tonic-gate 			HAT_UNLOAD|HAT_UNLOAD_OTHER);
2841*0Sstevel@tonic-gate 	}
2842*0Sstevel@tonic-gate 
2843*0Sstevel@tonic-gate 	return (0);
2844*0Sstevel@tonic-gate }
2845*0Sstevel@tonic-gate 
2846*0Sstevel@tonic-gate /*
2847*0Sstevel@tonic-gate  * calculates the optimal page size that will be used for hat_devload().
2848*0Sstevel@tonic-gate  */
2849*0Sstevel@tonic-gate static void
2850*0Sstevel@tonic-gate devmap_get_large_pgsize(devmap_handle_t *dhp, size_t len, caddr_t addr,
2851*0Sstevel@tonic-gate     size_t *llen, caddr_t *laddr)
2852*0Sstevel@tonic-gate {
2853*0Sstevel@tonic-gate 	ulong_t off;
2854*0Sstevel@tonic-gate 	ulong_t pfn;
2855*0Sstevel@tonic-gate 	ulong_t pgsize;
2856*0Sstevel@tonic-gate 	uint_t first = 1;
2857*0Sstevel@tonic-gate 
2858*0Sstevel@tonic-gate 	TRACE_0(TR_FAC_DEVMAP, TR_DEVMAP_GET_LARGE_PGSIZE,
2859*0Sstevel@tonic-gate 	    "devmap_get_large_pgsize:start");
2860*0Sstevel@tonic-gate 
2861*0Sstevel@tonic-gate 	/*
2862*0Sstevel@tonic-gate 	 * RFE - Code only supports large page mappings for devmem
2863*0Sstevel@tonic-gate 	 * This code could be changed in future if we want to support
2864*0Sstevel@tonic-gate 	 * large page mappings for kernel exported memory.
2865*0Sstevel@tonic-gate 	 */
2866*0Sstevel@tonic-gate 	ASSERT(dhp_is_devmem(dhp));
2867*0Sstevel@tonic-gate 	ASSERT(!(dhp->dh_flags & DEVMAP_MAPPING_INVALID));
2868*0Sstevel@tonic-gate 
2869*0Sstevel@tonic-gate 	*llen = 0;
2870*0Sstevel@tonic-gate 	off = (ulong_t)(addr - dhp->dh_uvaddr);
2871*0Sstevel@tonic-gate 	while ((long)len > 0) {
2872*0Sstevel@tonic-gate 		/*
2873*0Sstevel@tonic-gate 		 * get the optimal pfn to minimize address translations.
2874*0Sstevel@tonic-gate 		 * devmap_roundup() returns residue bytes for next round
2875*0Sstevel@tonic-gate 		 * calculations.
2876*0Sstevel@tonic-gate 		 */
2877*0Sstevel@tonic-gate 		len = devmap_roundup(dhp, off, len, &pfn, &pgsize);
2878*0Sstevel@tonic-gate 
2879*0Sstevel@tonic-gate 		if (first) {
2880*0Sstevel@tonic-gate 			*laddr = dhp->dh_uvaddr + ptob(pfn - dhp->dh_pfn);
2881*0Sstevel@tonic-gate 			first = 0;
2882*0Sstevel@tonic-gate 		}
2883*0Sstevel@tonic-gate 
2884*0Sstevel@tonic-gate 		*llen += pgsize;
2885*0Sstevel@tonic-gate 		off = ptob(pfn - dhp->dh_pfn) + pgsize;
2886*0Sstevel@tonic-gate 	}
2887*0Sstevel@tonic-gate 	/* Large page mapping len/addr cover more range than orginal fault */
2888*0Sstevel@tonic-gate 	ASSERT(*llen >= len && *laddr <= addr);
2889*0Sstevel@tonic-gate 	ASSERT((*laddr + *llen) >= (addr + len));
2890*0Sstevel@tonic-gate }
2891*0Sstevel@tonic-gate 
2892*0Sstevel@tonic-gate /*
2893*0Sstevel@tonic-gate  * Initialize the devmap_softlock structure.
2894*0Sstevel@tonic-gate  */
2895*0Sstevel@tonic-gate static struct devmap_softlock *
2896*0Sstevel@tonic-gate devmap_softlock_init(dev_t dev, ulong_t id)
2897*0Sstevel@tonic-gate {
2898*0Sstevel@tonic-gate 	struct devmap_softlock *slock;
2899*0Sstevel@tonic-gate 	struct devmap_softlock *tmp;
2900*0Sstevel@tonic-gate 
2901*0Sstevel@tonic-gate 	TRACE_0(TR_FAC_DEVMAP, TR_DEVMAP_SOFTLOCK_INIT,
2902*0Sstevel@tonic-gate 	    "devmap_softlock_init:start");
2903*0Sstevel@tonic-gate 
2904*0Sstevel@tonic-gate 	tmp = kmem_zalloc(sizeof (struct devmap_softlock), KM_SLEEP);
2905*0Sstevel@tonic-gate 	mutex_enter(&devmap_slock);
2906*0Sstevel@tonic-gate 
2907*0Sstevel@tonic-gate 	for (slock = devmap_slist; slock != NULL; slock = slock->next)
2908*0Sstevel@tonic-gate 		if ((slock->dev == dev) && (slock->id == id))
2909*0Sstevel@tonic-gate 			break;
2910*0Sstevel@tonic-gate 
2911*0Sstevel@tonic-gate 	if (slock == NULL) {
2912*0Sstevel@tonic-gate 		slock = tmp;
2913*0Sstevel@tonic-gate 		slock->dev = dev;
2914*0Sstevel@tonic-gate 		slock->id = id;
2915*0Sstevel@tonic-gate 		mutex_init(&slock->lock, NULL, MUTEX_DEFAULT, NULL);
2916*0Sstevel@tonic-gate 		cv_init(&slock->cv, NULL, CV_DEFAULT, NULL);
2917*0Sstevel@tonic-gate 		slock->next = devmap_slist;
2918*0Sstevel@tonic-gate 		devmap_slist = slock;
2919*0Sstevel@tonic-gate 	} else
2920*0Sstevel@tonic-gate 		kmem_free(tmp, sizeof (struct devmap_softlock));
2921*0Sstevel@tonic-gate 
2922*0Sstevel@tonic-gate 	mutex_enter(&slock->lock);
2923*0Sstevel@tonic-gate 	slock->refcnt++;
2924*0Sstevel@tonic-gate 	mutex_exit(&slock->lock);
2925*0Sstevel@tonic-gate 	mutex_exit(&devmap_slock);
2926*0Sstevel@tonic-gate 
2927*0Sstevel@tonic-gate 	return (slock);
2928*0Sstevel@tonic-gate }
2929*0Sstevel@tonic-gate 
2930*0Sstevel@tonic-gate /*
2931*0Sstevel@tonic-gate  * Wake up processes that sleep on softlocked.
2932*0Sstevel@tonic-gate  * Free dh_softlock if refcnt is 0.
2933*0Sstevel@tonic-gate  */
2934*0Sstevel@tonic-gate static void
2935*0Sstevel@tonic-gate devmap_softlock_rele(devmap_handle_t *dhp)
2936*0Sstevel@tonic-gate {
2937*0Sstevel@tonic-gate 	struct devmap_softlock *slock = dhp->dh_softlock;
2938*0Sstevel@tonic-gate 	struct devmap_softlock *tmp;
2939*0Sstevel@tonic-gate 	struct devmap_softlock *parent;
2940*0Sstevel@tonic-gate 
2941*0Sstevel@tonic-gate 	TRACE_0(TR_FAC_DEVMAP, TR_DEVMAP_SOFTLOCK_RELE,
2942*0Sstevel@tonic-gate 	    "devmap_softlock_rele:start");
2943*0Sstevel@tonic-gate 
2944*0Sstevel@tonic-gate 	mutex_enter(&devmap_slock);
2945*0Sstevel@tonic-gate 	mutex_enter(&slock->lock);
2946*0Sstevel@tonic-gate 
2947*0Sstevel@tonic-gate 	ASSERT(slock->refcnt > 0);
2948*0Sstevel@tonic-gate 
2949*0Sstevel@tonic-gate 	slock->refcnt--;
2950*0Sstevel@tonic-gate 
2951*0Sstevel@tonic-gate 	/*
2952*0Sstevel@tonic-gate 	 * If no one is using the device, free up the slock data.
2953*0Sstevel@tonic-gate 	 */
2954*0Sstevel@tonic-gate 	if (slock->refcnt == 0) {
2955*0Sstevel@tonic-gate 		slock->softlocked = 0;
2956*0Sstevel@tonic-gate 		cv_signal(&slock->cv);
2957*0Sstevel@tonic-gate 
2958*0Sstevel@tonic-gate 		if (devmap_slist == slock)
2959*0Sstevel@tonic-gate 			devmap_slist = slock->next;
2960*0Sstevel@tonic-gate 		else {
2961*0Sstevel@tonic-gate 			parent = devmap_slist;
2962*0Sstevel@tonic-gate 			for (tmp = devmap_slist->next; tmp != NULL;
2963*0Sstevel@tonic-gate 				tmp = tmp->next) {
2964*0Sstevel@tonic-gate 				if (tmp == slock) {
2965*0Sstevel@tonic-gate 					parent->next = tmp->next;
2966*0Sstevel@tonic-gate 					break;
2967*0Sstevel@tonic-gate 				}
2968*0Sstevel@tonic-gate 				parent = tmp;
2969*0Sstevel@tonic-gate 			}
2970*0Sstevel@tonic-gate 		}
2971*0Sstevel@tonic-gate 		mutex_exit(&slock->lock);
2972*0Sstevel@tonic-gate 		mutex_destroy(&slock->lock);
2973*0Sstevel@tonic-gate 		cv_destroy(&slock->cv);
2974*0Sstevel@tonic-gate 		kmem_free(slock, sizeof (struct devmap_softlock));
2975*0Sstevel@tonic-gate 	} else
2976*0Sstevel@tonic-gate 		mutex_exit(&slock->lock);
2977*0Sstevel@tonic-gate 
2978*0Sstevel@tonic-gate 	mutex_exit(&devmap_slock);
2979*0Sstevel@tonic-gate }
2980*0Sstevel@tonic-gate 
2981*0Sstevel@tonic-gate /*
2982*0Sstevel@tonic-gate  * Wake up processes that sleep on dh_ctx->locked.
2983*0Sstevel@tonic-gate  * Free dh_ctx if refcnt is 0.
2984*0Sstevel@tonic-gate  */
2985*0Sstevel@tonic-gate static void
2986*0Sstevel@tonic-gate devmap_ctx_rele(devmap_handle_t *dhp)
2987*0Sstevel@tonic-gate {
2988*0Sstevel@tonic-gate 	struct devmap_ctx *devctx = dhp->dh_ctx;
2989*0Sstevel@tonic-gate 	struct devmap_ctx *tmp;
2990*0Sstevel@tonic-gate 	struct devmap_ctx *parent;
2991*0Sstevel@tonic-gate 	timeout_id_t tid;
2992*0Sstevel@tonic-gate 
2993*0Sstevel@tonic-gate 	TRACE_0(TR_FAC_DEVMAP, TR_DEVMAP_CTX_RELE,
2994*0Sstevel@tonic-gate 	    "devmap_ctx_rele:start");
2995*0Sstevel@tonic-gate 
2996*0Sstevel@tonic-gate 	mutex_enter(&devmapctx_lock);
2997*0Sstevel@tonic-gate 	mutex_enter(&devctx->lock);
2998*0Sstevel@tonic-gate 
2999*0Sstevel@tonic-gate 	ASSERT(devctx->refcnt > 0);
3000*0Sstevel@tonic-gate 
3001*0Sstevel@tonic-gate 	devctx->refcnt--;
3002*0Sstevel@tonic-gate 
3003*0Sstevel@tonic-gate 	/*
3004*0Sstevel@tonic-gate 	 * If no one is using the device, free up the devctx data.
3005*0Sstevel@tonic-gate 	 */
3006*0Sstevel@tonic-gate 	if (devctx->refcnt == 0) {
3007*0Sstevel@tonic-gate 		/*
3008*0Sstevel@tonic-gate 		 * Untimeout any threads using this mapping as they are about
3009*0Sstevel@tonic-gate 		 * to go away.
3010*0Sstevel@tonic-gate 		 */
3011*0Sstevel@tonic-gate 		if (devctx->timeout != 0) {
3012*0Sstevel@tonic-gate 			TRACE_0(TR_FAC_DEVMAP, TR_DEVMAP_CTX_RELE_CK1,
3013*0Sstevel@tonic-gate 			    "devmap_ctx_rele:untimeout ctx->timeout");
3014*0Sstevel@tonic-gate 
3015*0Sstevel@tonic-gate 			tid = devctx->timeout;
3016*0Sstevel@tonic-gate 			mutex_exit(&devctx->lock);
3017*0Sstevel@tonic-gate 			(void) untimeout(tid);
3018*0Sstevel@tonic-gate 			mutex_enter(&devctx->lock);
3019*0Sstevel@tonic-gate 		}
3020*0Sstevel@tonic-gate 
3021*0Sstevel@tonic-gate 		devctx->oncpu = 0;
3022*0Sstevel@tonic-gate 		cv_signal(&devctx->cv);
3023*0Sstevel@tonic-gate 
3024*0Sstevel@tonic-gate 		if (devmapctx_list == devctx)
3025*0Sstevel@tonic-gate 			devmapctx_list = devctx->next;
3026*0Sstevel@tonic-gate 		else {
3027*0Sstevel@tonic-gate 			parent = devmapctx_list;
3028*0Sstevel@tonic-gate 			for (tmp = devmapctx_list->next; tmp != NULL;
3029*0Sstevel@tonic-gate 				tmp = tmp->next) {
3030*0Sstevel@tonic-gate 				if (tmp == devctx) {
3031*0Sstevel@tonic-gate 					parent->next = tmp->next;
3032*0Sstevel@tonic-gate 					break;
3033*0Sstevel@tonic-gate 				}
3034*0Sstevel@tonic-gate 				parent = tmp;
3035*0Sstevel@tonic-gate 			}
3036*0Sstevel@tonic-gate 		}
3037*0Sstevel@tonic-gate 		mutex_exit(&devctx->lock);
3038*0Sstevel@tonic-gate 		mutex_destroy(&devctx->lock);
3039*0Sstevel@tonic-gate 		cv_destroy(&devctx->cv);
3040*0Sstevel@tonic-gate 		kmem_free(devctx, sizeof (struct devmap_ctx));
3041*0Sstevel@tonic-gate 	} else
3042*0Sstevel@tonic-gate 		mutex_exit(&devctx->lock);
3043*0Sstevel@tonic-gate 
3044*0Sstevel@tonic-gate 	mutex_exit(&devmapctx_lock);
3045*0Sstevel@tonic-gate }
3046*0Sstevel@tonic-gate 
3047*0Sstevel@tonic-gate /*
3048*0Sstevel@tonic-gate  * devmap_load:
3049*0Sstevel@tonic-gate  *			Marks a segdev segment or pages if offset->offset+len
3050*0Sstevel@tonic-gate  *			is not the entire segment as nointercept and faults in
3051*0Sstevel@tonic-gate  *			the pages in the range offset -> offset+len.
3052*0Sstevel@tonic-gate  */
3053*0Sstevel@tonic-gate int
3054*0Sstevel@tonic-gate devmap_load(devmap_cookie_t dhc, offset_t offset, size_t len, uint_t type,
3055*0Sstevel@tonic-gate     uint_t rw)
3056*0Sstevel@tonic-gate {
3057*0Sstevel@tonic-gate 	devmap_handle_t *dhp = (devmap_handle_t *)dhc;
3058*0Sstevel@tonic-gate 	struct as *asp = dhp->dh_seg->s_as;
3059*0Sstevel@tonic-gate 	caddr_t	addr;
3060*0Sstevel@tonic-gate 	ulong_t	size;
3061*0Sstevel@tonic-gate 	ssize_t	soff;	/* offset from the beginning of the segment */
3062*0Sstevel@tonic-gate 	int rc;
3063*0Sstevel@tonic-gate 
3064*0Sstevel@tonic-gate 	TRACE_3(TR_FAC_DEVMAP, TR_DEVMAP_LOAD,
3065*0Sstevel@tonic-gate 	    "devmap_load:start dhp=%p offset=%llx len=%lx",
3066*0Sstevel@tonic-gate 		(void *)dhp, offset, len);
3067*0Sstevel@tonic-gate 
3068*0Sstevel@tonic-gate 	DEBUGF(7, (CE_CONT, "devmap_load: dhp %p offset %llx len %lx\n",
3069*0Sstevel@tonic-gate 	    (void *)dhp, offset, len));
3070*0Sstevel@tonic-gate 
3071*0Sstevel@tonic-gate 	/*
3072*0Sstevel@tonic-gate 	 *	Hat layer only supports devload to process' context for which
3073*0Sstevel@tonic-gate 	 *	the as lock is held. Verify here and return error if drivers
3074*0Sstevel@tonic-gate 	 *	inadvertently call devmap_load on a wrong devmap handle.
3075*0Sstevel@tonic-gate 	 */
3076*0Sstevel@tonic-gate 	if ((asp != &kas) && !AS_LOCK_HELD(asp, &asp->a_lock))
3077*0Sstevel@tonic-gate 		return (FC_MAKE_ERR(EINVAL));
3078*0Sstevel@tonic-gate 
3079*0Sstevel@tonic-gate 	soff = (ssize_t)(offset - dhp->dh_uoff);
3080*0Sstevel@tonic-gate 	soff = round_down_p2(soff, PAGESIZE);
3081*0Sstevel@tonic-gate 	if (soff < 0 || soff >= dhp->dh_len)
3082*0Sstevel@tonic-gate 		return (FC_MAKE_ERR(EINVAL));
3083*0Sstevel@tonic-gate 
3084*0Sstevel@tonic-gate 	/*
3085*0Sstevel@tonic-gate 	 * Address and size must be page aligned.  Len is set to the
3086*0Sstevel@tonic-gate 	 * number of bytes in the number of pages that are required to
3087*0Sstevel@tonic-gate 	 * support len.  Offset is set to the byte offset of the first byte
3088*0Sstevel@tonic-gate 	 * of the page that contains offset.
3089*0Sstevel@tonic-gate 	 */
3090*0Sstevel@tonic-gate 	len = round_up_p2(len, PAGESIZE);
3091*0Sstevel@tonic-gate 
3092*0Sstevel@tonic-gate 	/*
3093*0Sstevel@tonic-gate 	 * If len == 0, then calculate the size by getting
3094*0Sstevel@tonic-gate 	 * the number of bytes from offset to the end of the segment.
3095*0Sstevel@tonic-gate 	 */
3096*0Sstevel@tonic-gate 	if (len == 0)
3097*0Sstevel@tonic-gate 		size = dhp->dh_len - soff;
3098*0Sstevel@tonic-gate 	else {
3099*0Sstevel@tonic-gate 		size = len;
3100*0Sstevel@tonic-gate 		if ((soff + size) > dhp->dh_len)
3101*0Sstevel@tonic-gate 			return (FC_MAKE_ERR(EINVAL));
3102*0Sstevel@tonic-gate 	}
3103*0Sstevel@tonic-gate 
3104*0Sstevel@tonic-gate 	/*
3105*0Sstevel@tonic-gate 	 * The address is offset bytes from the base address of
3106*0Sstevel@tonic-gate 	 * the segment.
3107*0Sstevel@tonic-gate 	 */
3108*0Sstevel@tonic-gate 	addr = (caddr_t)(soff + dhp->dh_uvaddr);
3109*0Sstevel@tonic-gate 
3110*0Sstevel@tonic-gate 	HOLD_DHP_LOCK(dhp);
3111*0Sstevel@tonic-gate 	rc = segdev_faultpages(asp->a_hat,
3112*0Sstevel@tonic-gate 			dhp->dh_seg, addr, size, type, rw, dhp);
3113*0Sstevel@tonic-gate 	RELE_DHP_LOCK(dhp);
3114*0Sstevel@tonic-gate 	return (rc);
3115*0Sstevel@tonic-gate }
3116*0Sstevel@tonic-gate 
3117*0Sstevel@tonic-gate int
3118*0Sstevel@tonic-gate devmap_setup(dev_t dev, offset_t off, struct as *as, caddr_t *addrp,
3119*0Sstevel@tonic-gate     size_t len, uint_t prot, uint_t maxprot, uint_t flags, struct cred *cred)
3120*0Sstevel@tonic-gate {
3121*0Sstevel@tonic-gate 	register devmap_handle_t *dhp;
3122*0Sstevel@tonic-gate 	int (*devmap)(dev_t, devmap_cookie_t, offset_t, size_t,
3123*0Sstevel@tonic-gate 		size_t *, uint_t);
3124*0Sstevel@tonic-gate 	int (*mmap)(dev_t, off_t, int);
3125*0Sstevel@tonic-gate 	struct devmap_callback_ctl *callbackops;
3126*0Sstevel@tonic-gate 	devmap_handle_t *dhp_head = NULL;
3127*0Sstevel@tonic-gate 	devmap_handle_t *dhp_prev = NULL;
3128*0Sstevel@tonic-gate 	devmap_handle_t *dhp_curr;
3129*0Sstevel@tonic-gate 	caddr_t addr;
3130*0Sstevel@tonic-gate 	int map_flag;
3131*0Sstevel@tonic-gate 	int ret;
3132*0Sstevel@tonic-gate 	ulong_t total_len;
3133*0Sstevel@tonic-gate 	size_t map_len;
3134*0Sstevel@tonic-gate 	size_t resid_len = len;
3135*0Sstevel@tonic-gate 	offset_t map_off = off;
3136*0Sstevel@tonic-gate 	struct devmap_softlock *slock = NULL;
3137*0Sstevel@tonic-gate 
3138*0Sstevel@tonic-gate #ifdef lint
3139*0Sstevel@tonic-gate 	cred = cred;
3140*0Sstevel@tonic-gate #endif
3141*0Sstevel@tonic-gate 
3142*0Sstevel@tonic-gate 	TRACE_2(TR_FAC_DEVMAP, TR_DEVMAP_SETUP,
3143*0Sstevel@tonic-gate 	    "devmap_setup:start off=%llx len=%lx", off, len);
3144*0Sstevel@tonic-gate 	DEBUGF(3, (CE_CONT, "devmap_setup: off %llx len %lx\n",
3145*0Sstevel@tonic-gate 	    off, len));
3146*0Sstevel@tonic-gate 
3147*0Sstevel@tonic-gate 	devmap = devopsp[getmajor(dev)]->devo_cb_ops->cb_devmap;
3148*0Sstevel@tonic-gate 	mmap = devopsp[getmajor(dev)]->devo_cb_ops->cb_mmap;
3149*0Sstevel@tonic-gate 
3150*0Sstevel@tonic-gate 	/*
3151*0Sstevel@tonic-gate 	 * driver must provide devmap(9E) entry point in cb_ops to use the
3152*0Sstevel@tonic-gate 	 * devmap framework.
3153*0Sstevel@tonic-gate 	 */
3154*0Sstevel@tonic-gate 	if (devmap == NULL || devmap == nulldev || devmap == nodev)
3155*0Sstevel@tonic-gate 		return (EINVAL);
3156*0Sstevel@tonic-gate 
3157*0Sstevel@tonic-gate 	/*
3158*0Sstevel@tonic-gate 	 * To protect from an inadvertent entry because the devmap entry point
3159*0Sstevel@tonic-gate 	 * is not NULL, return error if D_DEVMAP bit is not set in cb_flag and
3160*0Sstevel@tonic-gate 	 * mmap is NULL.
3161*0Sstevel@tonic-gate 	 */
3162*0Sstevel@tonic-gate 	map_flag = devopsp[getmajor(dev)]->devo_cb_ops->cb_flag;
3163*0Sstevel@tonic-gate 	if ((map_flag & D_DEVMAP) == 0 && (mmap == NULL || mmap == nulldev))
3164*0Sstevel@tonic-gate 		return (EINVAL);
3165*0Sstevel@tonic-gate 
3166*0Sstevel@tonic-gate 	/*
3167*0Sstevel@tonic-gate 	 * devmap allows mmap(2) to map multiple registers.
3168*0Sstevel@tonic-gate 	 * one devmap_handle is created for each register mapped.
3169*0Sstevel@tonic-gate 	 */
3170*0Sstevel@tonic-gate 	for (total_len = 0; total_len < len; total_len += map_len) {
3171*0Sstevel@tonic-gate 		dhp = kmem_zalloc(sizeof (devmap_handle_t), KM_SLEEP);
3172*0Sstevel@tonic-gate 
3173*0Sstevel@tonic-gate 		if (dhp_prev != NULL)
3174*0Sstevel@tonic-gate 			dhp_prev->dh_next = dhp;
3175*0Sstevel@tonic-gate 		else
3176*0Sstevel@tonic-gate 			dhp_head = dhp;
3177*0Sstevel@tonic-gate 		dhp_prev = dhp;
3178*0Sstevel@tonic-gate 
3179*0Sstevel@tonic-gate 		dhp->dh_prot = prot;
3180*0Sstevel@tonic-gate 		dhp->dh_orig_maxprot = dhp->dh_maxprot = maxprot;
3181*0Sstevel@tonic-gate 		dhp->dh_dev = dev;
3182*0Sstevel@tonic-gate 		dhp->dh_timeout_length = CTX_TIMEOUT_VALUE;
3183*0Sstevel@tonic-gate 		dhp->dh_uoff = map_off;
3184*0Sstevel@tonic-gate 
3185*0Sstevel@tonic-gate 		/*
3186*0Sstevel@tonic-gate 		 * Get mapping specific info from
3187*0Sstevel@tonic-gate 		 * the driver, such as rnumber, roff, len, callbackops,
3188*0Sstevel@tonic-gate 		 * accattrp and, if the mapping is for kernel memory,
3189*0Sstevel@tonic-gate 		 * ddi_umem_cookie.
3190*0Sstevel@tonic-gate 		 */
3191*0Sstevel@tonic-gate 		if ((ret = cdev_devmap(dev, dhp, map_off,
3192*0Sstevel@tonic-gate 		    resid_len, &map_len, get_udatamodel())) != 0) {
3193*0Sstevel@tonic-gate 			free_devmap_handle(dhp_head);
3194*0Sstevel@tonic-gate 			return (ENXIO);
3195*0Sstevel@tonic-gate 		}
3196*0Sstevel@tonic-gate 
3197*0Sstevel@tonic-gate 		if (map_len & PAGEOFFSET) {
3198*0Sstevel@tonic-gate 			free_devmap_handle(dhp_head);
3199*0Sstevel@tonic-gate 			return (EINVAL);
3200*0Sstevel@tonic-gate 		}
3201*0Sstevel@tonic-gate 
3202*0Sstevel@tonic-gate 		callbackops = &dhp->dh_callbackops;
3203*0Sstevel@tonic-gate 
3204*0Sstevel@tonic-gate 		if ((callbackops->devmap_access == NULL) ||
3205*0Sstevel@tonic-gate 			(callbackops->devmap_access == nulldev) ||
3206*0Sstevel@tonic-gate 			(callbackops->devmap_access == nodev)) {
3207*0Sstevel@tonic-gate 			/*
3208*0Sstevel@tonic-gate 			 * Normally devmap does not support MAP_PRIVATE unless
3209*0Sstevel@tonic-gate 			 * the drivers provide a valid devmap_access routine.
3210*0Sstevel@tonic-gate 			 */
3211*0Sstevel@tonic-gate 			if ((flags & MAP_PRIVATE) != 0) {
3212*0Sstevel@tonic-gate 				free_devmap_handle(dhp_head);
3213*0Sstevel@tonic-gate 				return (EINVAL);
3214*0Sstevel@tonic-gate 			}
3215*0Sstevel@tonic-gate 		} else {
3216*0Sstevel@tonic-gate 			/*
3217*0Sstevel@tonic-gate 			 * Initialize dhp_softlock and dh_ctx if the drivers
3218*0Sstevel@tonic-gate 			 * provide devmap_access.
3219*0Sstevel@tonic-gate 			 */
3220*0Sstevel@tonic-gate 			dhp->dh_softlock = devmap_softlock_init(dev,
3221*0Sstevel@tonic-gate 				(ulong_t)callbackops->devmap_access);
3222*0Sstevel@tonic-gate 			dhp->dh_ctx = devmap_ctxinit(dev,
3223*0Sstevel@tonic-gate 				(ulong_t)callbackops->devmap_access);
3224*0Sstevel@tonic-gate 
3225*0Sstevel@tonic-gate 			/*
3226*0Sstevel@tonic-gate 			 * segdev_fault can only work when all
3227*0Sstevel@tonic-gate 			 * dh_softlock in a multi-dhp mapping
3228*0Sstevel@tonic-gate 			 * are same. see comments in segdev_fault
3229*0Sstevel@tonic-gate 			 * This code keeps track of the first
3230*0Sstevel@tonic-gate 			 * dh_softlock allocated in slock and
3231*0Sstevel@tonic-gate 			 * compares all later allocations and if
3232*0Sstevel@tonic-gate 			 * not similar, returns an error.
3233*0Sstevel@tonic-gate 			 */
3234*0Sstevel@tonic-gate 			if (slock == NULL)
3235*0Sstevel@tonic-gate 				slock = dhp->dh_softlock;
3236*0Sstevel@tonic-gate 			if (slock != dhp->dh_softlock) {
3237*0Sstevel@tonic-gate 				free_devmap_handle(dhp_head);
3238*0Sstevel@tonic-gate 				return (ENOTSUP);
3239*0Sstevel@tonic-gate 			}
3240*0Sstevel@tonic-gate 		}
3241*0Sstevel@tonic-gate 
3242*0Sstevel@tonic-gate 		map_off += map_len;
3243*0Sstevel@tonic-gate 		resid_len -= map_len;
3244*0Sstevel@tonic-gate 	}
3245*0Sstevel@tonic-gate 
3246*0Sstevel@tonic-gate 	/*
3247*0Sstevel@tonic-gate 	 * get the user virtual address and establish the mapping between
3248*0Sstevel@tonic-gate 	 * uvaddr and device physical address.
3249*0Sstevel@tonic-gate 	 */
3250*0Sstevel@tonic-gate 	if ((ret = devmap_device(dhp_head, as, addrp, off, len, flags))
3251*0Sstevel@tonic-gate 			!= 0) {
3252*0Sstevel@tonic-gate 		/*
3253*0Sstevel@tonic-gate 		 * free devmap handles if error during the mapping.
3254*0Sstevel@tonic-gate 		 */
3255*0Sstevel@tonic-gate 		free_devmap_handle(dhp_head);
3256*0Sstevel@tonic-gate 
3257*0Sstevel@tonic-gate 		return (ret);
3258*0Sstevel@tonic-gate 	}
3259*0Sstevel@tonic-gate 
3260*0Sstevel@tonic-gate 	/*
3261*0Sstevel@tonic-gate 	 * call the driver's devmap_map callback to do more after the mapping,
3262*0Sstevel@tonic-gate 	 * such as to allocate driver private data for context management.
3263*0Sstevel@tonic-gate 	 */
3264*0Sstevel@tonic-gate 	dhp = dhp_head;
3265*0Sstevel@tonic-gate 	map_off = off;
3266*0Sstevel@tonic-gate 	addr = *addrp;
3267*0Sstevel@tonic-gate 	while (dhp != NULL) {
3268*0Sstevel@tonic-gate 		callbackops = &dhp->dh_callbackops;
3269*0Sstevel@tonic-gate 		dhp->dh_uvaddr = addr;
3270*0Sstevel@tonic-gate 		dhp_curr = dhp;
3271*0Sstevel@tonic-gate 		if (callbackops->devmap_map != NULL) {
3272*0Sstevel@tonic-gate 			ret = (*callbackops->devmap_map)((devmap_cookie_t)dhp,
3273*0Sstevel@tonic-gate 					dev, flags, map_off,
3274*0Sstevel@tonic-gate 					dhp->dh_len, &dhp->dh_pvtp);
3275*0Sstevel@tonic-gate 			if (ret != 0) {
3276*0Sstevel@tonic-gate 				struct segdev_data *sdp;
3277*0Sstevel@tonic-gate 
3278*0Sstevel@tonic-gate 				/*
3279*0Sstevel@tonic-gate 				 * call driver's devmap_unmap entry point
3280*0Sstevel@tonic-gate 				 * to free driver resources.
3281*0Sstevel@tonic-gate 				 */
3282*0Sstevel@tonic-gate 				dhp = dhp_head;
3283*0Sstevel@tonic-gate 				map_off = off;
3284*0Sstevel@tonic-gate 				while (dhp != dhp_curr) {
3285*0Sstevel@tonic-gate 					callbackops = &dhp->dh_callbackops;
3286*0Sstevel@tonic-gate 					if (callbackops->devmap_unmap != NULL) {
3287*0Sstevel@tonic-gate 						(*callbackops->devmap_unmap)(
3288*0Sstevel@tonic-gate 							dhp, dhp->dh_pvtp,
3289*0Sstevel@tonic-gate 							map_off, dhp->dh_len,
3290*0Sstevel@tonic-gate 							NULL, NULL, NULL, NULL);
3291*0Sstevel@tonic-gate 					}
3292*0Sstevel@tonic-gate 					map_off += dhp->dh_len;
3293*0Sstevel@tonic-gate 					dhp = dhp->dh_next;
3294*0Sstevel@tonic-gate 				}
3295*0Sstevel@tonic-gate 				sdp = dhp_head->dh_seg->s_data;
3296*0Sstevel@tonic-gate 				sdp->devmap_data = NULL;
3297*0Sstevel@tonic-gate 				free_devmap_handle(dhp_head);
3298*0Sstevel@tonic-gate 				return (ENXIO);
3299*0Sstevel@tonic-gate 			}
3300*0Sstevel@tonic-gate 		}
3301*0Sstevel@tonic-gate 		map_off += dhp->dh_len;
3302*0Sstevel@tonic-gate 		addr += dhp->dh_len;
3303*0Sstevel@tonic-gate 		dhp = dhp->dh_next;
3304*0Sstevel@tonic-gate 	}
3305*0Sstevel@tonic-gate 
3306*0Sstevel@tonic-gate 	return (0);
3307*0Sstevel@tonic-gate }
3308*0Sstevel@tonic-gate 
3309*0Sstevel@tonic-gate int
3310*0Sstevel@tonic-gate ddi_devmap_segmap(dev_t dev, off_t off, ddi_as_handle_t as, caddr_t *addrp,
3311*0Sstevel@tonic-gate     off_t len, uint_t prot, uint_t maxprot, uint_t flags, struct cred *cred)
3312*0Sstevel@tonic-gate {
3313*0Sstevel@tonic-gate 	TRACE_0(TR_FAC_DEVMAP, TR_DEVMAP_SEGMAP,
3314*0Sstevel@tonic-gate 	    "devmap_segmap:start");
3315*0Sstevel@tonic-gate 	return (devmap_setup(dev, (offset_t)off, (struct as *)as, addrp,
3316*0Sstevel@tonic-gate 	    (size_t)len, prot, maxprot, flags, cred));
3317*0Sstevel@tonic-gate }
3318*0Sstevel@tonic-gate 
3319*0Sstevel@tonic-gate /*
3320*0Sstevel@tonic-gate  * Called from devmap_devmem_setup/remap to see if can use large pages for
3321*0Sstevel@tonic-gate  * this device mapping.
3322*0Sstevel@tonic-gate  * Also calculate the max. page size for this mapping.
3323*0Sstevel@tonic-gate  * this page size will be used in fault routine for
3324*0Sstevel@tonic-gate  * optimal page size calculations.
3325*0Sstevel@tonic-gate  */
3326*0Sstevel@tonic-gate static void
3327*0Sstevel@tonic-gate devmap_devmem_large_page_setup(devmap_handle_t *dhp)
3328*0Sstevel@tonic-gate {
3329*0Sstevel@tonic-gate 	ASSERT(dhp_is_devmem(dhp));
3330*0Sstevel@tonic-gate 	dhp->dh_mmulevel = 0;
3331*0Sstevel@tonic-gate 
3332*0Sstevel@tonic-gate 	/*
3333*0Sstevel@tonic-gate 	 * use large page size only if:
3334*0Sstevel@tonic-gate 	 *  1. device memory.
3335*0Sstevel@tonic-gate 	 *  2. mmu supports multiple page sizes,
3336*0Sstevel@tonic-gate 	 *  3. Driver did not disallow it
3337*0Sstevel@tonic-gate 	 *  4. dhp length is at least as big as the large pagesize
3338*0Sstevel@tonic-gate 	 *  5. the uvaddr and pfn are large pagesize aligned
3339*0Sstevel@tonic-gate 	 */
3340*0Sstevel@tonic-gate 	if (page_num_pagesizes() > 1 &&
3341*0Sstevel@tonic-gate 	    !(dhp->dh_flags & (DEVMAP_USE_PAGESIZE | DEVMAP_MAPPING_INVALID))) {
3342*0Sstevel@tonic-gate 		ulong_t base;
3343*0Sstevel@tonic-gate 		int level;
3344*0Sstevel@tonic-gate 
3345*0Sstevel@tonic-gate 		base = (ulong_t)ptob(dhp->dh_pfn);
3346*0Sstevel@tonic-gate 		for (level = 1; level < page_num_pagesizes(); level++) {
3347*0Sstevel@tonic-gate 			size_t pgsize = page_get_pagesize(level);
3348*0Sstevel@tonic-gate 			if ((dhp->dh_len < pgsize) ||
3349*0Sstevel@tonic-gate 			    (!VA_PA_PGSIZE_ALIGNED((uintptr_t)dhp->dh_uvaddr,
3350*0Sstevel@tonic-gate 					base, pgsize))) {
3351*0Sstevel@tonic-gate 				break;
3352*0Sstevel@tonic-gate 			}
3353*0Sstevel@tonic-gate 		}
3354*0Sstevel@tonic-gate 		dhp->dh_mmulevel = level - 1;
3355*0Sstevel@tonic-gate 	}
3356*0Sstevel@tonic-gate 	if (dhp->dh_mmulevel > 0) {
3357*0Sstevel@tonic-gate 		dhp->dh_flags |= DEVMAP_FLAG_LARGE;
3358*0Sstevel@tonic-gate 	} else {
3359*0Sstevel@tonic-gate 		dhp->dh_flags &= ~DEVMAP_FLAG_LARGE;
3360*0Sstevel@tonic-gate 	}
3361*0Sstevel@tonic-gate }
3362*0Sstevel@tonic-gate 
3363*0Sstevel@tonic-gate /*
3364*0Sstevel@tonic-gate  * Called by driver devmap routine to pass device specific info to
3365*0Sstevel@tonic-gate  * the framework.    used for device memory mapping only.
3366*0Sstevel@tonic-gate  */
3367*0Sstevel@tonic-gate int
3368*0Sstevel@tonic-gate devmap_devmem_setup(devmap_cookie_t dhc, dev_info_t *dip,
3369*0Sstevel@tonic-gate     struct devmap_callback_ctl *callbackops, uint_t rnumber, offset_t roff,
3370*0Sstevel@tonic-gate     size_t len, uint_t maxprot, uint_t flags, ddi_device_acc_attr_t *accattrp)
3371*0Sstevel@tonic-gate {
3372*0Sstevel@tonic-gate 	devmap_handle_t *dhp = (devmap_handle_t *)dhc;
3373*0Sstevel@tonic-gate 	ddi_acc_handle_t handle;
3374*0Sstevel@tonic-gate 	ddi_map_req_t mr;
3375*0Sstevel@tonic-gate 	ddi_acc_hdl_t *hp;
3376*0Sstevel@tonic-gate 	int err;
3377*0Sstevel@tonic-gate 
3378*0Sstevel@tonic-gate 	TRACE_4(TR_FAC_DEVMAP, TR_DEVMAP_DEVMEM_SETUP,
3379*0Sstevel@tonic-gate 	    "devmap_devmem_setup:start dhp=%p offset=%llx rnum=%d len=%lx",
3380*0Sstevel@tonic-gate 	    (void *)dhp, roff, rnumber, (uint_t)len);
3381*0Sstevel@tonic-gate 	DEBUGF(2, (CE_CONT, "devmap_devmem_setup: dhp %p offset %llx "
3382*0Sstevel@tonic-gate 	    "rnum %d len %lx\n", (void *)dhp, roff, rnumber, len));
3383*0Sstevel@tonic-gate 
3384*0Sstevel@tonic-gate 	/*
3385*0Sstevel@tonic-gate 	 * First to check if this function has been called for this dhp.
3386*0Sstevel@tonic-gate 	 */
3387*0Sstevel@tonic-gate 	if (dhp->dh_flags & DEVMAP_SETUP_DONE)
3388*0Sstevel@tonic-gate 		return (DDI_FAILURE);
3389*0Sstevel@tonic-gate 
3390*0Sstevel@tonic-gate 	if ((dhp->dh_prot & dhp->dh_orig_maxprot & maxprot) != dhp->dh_prot)
3391*0Sstevel@tonic-gate 		return (DDI_FAILURE);
3392*0Sstevel@tonic-gate 
3393*0Sstevel@tonic-gate 	if (flags & DEVMAP_MAPPING_INVALID) {
3394*0Sstevel@tonic-gate 		/*
3395*0Sstevel@tonic-gate 		 * Don't go up the tree to get pfn if the driver specifies
3396*0Sstevel@tonic-gate 		 * DEVMAP_MAPPING_INVALID in flags.
3397*0Sstevel@tonic-gate 		 *
3398*0Sstevel@tonic-gate 		 * If DEVMAP_MAPPING_INVALID is specified, we have to grant
3399*0Sstevel@tonic-gate 		 * remap permission.
3400*0Sstevel@tonic-gate 		 */
3401*0Sstevel@tonic-gate 		if (!(flags & DEVMAP_ALLOW_REMAP)) {
3402*0Sstevel@tonic-gate 			return (DDI_FAILURE);
3403*0Sstevel@tonic-gate 		}
3404*0Sstevel@tonic-gate 		dhp->dh_pfn = PFN_INVALID;
3405*0Sstevel@tonic-gate 	} else {
3406*0Sstevel@tonic-gate 		handle = impl_acc_hdl_alloc(KM_SLEEP, NULL);
3407*0Sstevel@tonic-gate 		if (handle == NULL)
3408*0Sstevel@tonic-gate 			return (DDI_FAILURE);
3409*0Sstevel@tonic-gate 
3410*0Sstevel@tonic-gate 		hp = impl_acc_hdl_get(handle);
3411*0Sstevel@tonic-gate 		hp->ah_vers = VERS_ACCHDL;
3412*0Sstevel@tonic-gate 		hp->ah_dip = dip;
3413*0Sstevel@tonic-gate 		hp->ah_rnumber = rnumber;
3414*0Sstevel@tonic-gate 		hp->ah_offset = roff;
3415*0Sstevel@tonic-gate 		hp->ah_len = len;
3416*0Sstevel@tonic-gate 		if (accattrp != NULL)
3417*0Sstevel@tonic-gate 			hp->ah_acc = *accattrp;
3418*0Sstevel@tonic-gate 
3419*0Sstevel@tonic-gate 		mr.map_op = DDI_MO_MAP_LOCKED;
3420*0Sstevel@tonic-gate 		mr.map_type = DDI_MT_RNUMBER;
3421*0Sstevel@tonic-gate 		mr.map_obj.rnumber = rnumber;
3422*0Sstevel@tonic-gate 		mr.map_prot = maxprot & dhp->dh_orig_maxprot;
3423*0Sstevel@tonic-gate 		mr.map_flags = DDI_MF_DEVICE_MAPPING;
3424*0Sstevel@tonic-gate 		mr.map_handlep = hp;
3425*0Sstevel@tonic-gate 		mr.map_vers = DDI_MAP_VERSION;
3426*0Sstevel@tonic-gate 
3427*0Sstevel@tonic-gate 		/*
3428*0Sstevel@tonic-gate 		 * up the device tree to get pfn.
3429*0Sstevel@tonic-gate 		 * The rootnex_map_regspec() routine in nexus drivers has been
3430*0Sstevel@tonic-gate 		 * modified to return pfn if map_flags is DDI_MF_DEVICE_MAPPING.
3431*0Sstevel@tonic-gate 		 */
3432*0Sstevel@tonic-gate 		err = ddi_map(dip, &mr, roff, len, (caddr_t *)&dhp->dh_pfn);
3433*0Sstevel@tonic-gate 		dhp->dh_hat_attr = hp->ah_hat_flags;
3434*0Sstevel@tonic-gate 		impl_acc_hdl_free(handle);
3435*0Sstevel@tonic-gate 
3436*0Sstevel@tonic-gate 		if (err)
3437*0Sstevel@tonic-gate 			return (DDI_FAILURE);
3438*0Sstevel@tonic-gate 	}
3439*0Sstevel@tonic-gate 	/* Should not be using devmem setup for memory pages */
3440*0Sstevel@tonic-gate 	ASSERT(!pf_is_memory(dhp->dh_pfn));
3441*0Sstevel@tonic-gate 
3442*0Sstevel@tonic-gate 	/* Only some of the flags bits are settable by the driver */
3443*0Sstevel@tonic-gate 	dhp->dh_flags |= (flags & DEVMAP_SETUP_FLAGS);
3444*0Sstevel@tonic-gate 	dhp->dh_len = ptob(btopr(len));
3445*0Sstevel@tonic-gate 
3446*0Sstevel@tonic-gate 	dhp->dh_cookie = DEVMAP_DEVMEM_COOKIE;
3447*0Sstevel@tonic-gate 	dhp->dh_roff = ptob(btop(roff));
3448*0Sstevel@tonic-gate 
3449*0Sstevel@tonic-gate 	/* setup the dh_mmulevel and DEVMAP_FLAG_LARGE */
3450*0Sstevel@tonic-gate 	devmap_devmem_large_page_setup(dhp);
3451*0Sstevel@tonic-gate 	dhp->dh_maxprot = maxprot & dhp->dh_orig_maxprot;
3452*0Sstevel@tonic-gate 	ASSERT((dhp->dh_prot & dhp->dh_orig_maxprot & maxprot) == dhp->dh_prot);
3453*0Sstevel@tonic-gate 
3454*0Sstevel@tonic-gate 
3455*0Sstevel@tonic-gate 	if (callbackops != NULL) {
3456*0Sstevel@tonic-gate 		bcopy(callbackops, &dhp->dh_callbackops,
3457*0Sstevel@tonic-gate 		    sizeof (struct devmap_callback_ctl));
3458*0Sstevel@tonic-gate 	}
3459*0Sstevel@tonic-gate 
3460*0Sstevel@tonic-gate 	/*
3461*0Sstevel@tonic-gate 	 * Initialize dh_lock if we want to do remap.
3462*0Sstevel@tonic-gate 	 */
3463*0Sstevel@tonic-gate 	if (dhp->dh_flags & DEVMAP_ALLOW_REMAP) {
3464*0Sstevel@tonic-gate 		mutex_init(&dhp->dh_lock, NULL, MUTEX_DEFAULT, NULL);
3465*0Sstevel@tonic-gate 		dhp->dh_flags |= DEVMAP_LOCK_INITED;
3466*0Sstevel@tonic-gate 	}
3467*0Sstevel@tonic-gate 
3468*0Sstevel@tonic-gate 	dhp->dh_flags |= DEVMAP_SETUP_DONE;
3469*0Sstevel@tonic-gate 
3470*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
3471*0Sstevel@tonic-gate }
3472*0Sstevel@tonic-gate 
3473*0Sstevel@tonic-gate int
3474*0Sstevel@tonic-gate devmap_devmem_remap(devmap_cookie_t dhc, dev_info_t *dip,
3475*0Sstevel@tonic-gate     uint_t rnumber, offset_t roff, size_t len, uint_t maxprot,
3476*0Sstevel@tonic-gate     uint_t flags, ddi_device_acc_attr_t *accattrp)
3477*0Sstevel@tonic-gate {
3478*0Sstevel@tonic-gate 	devmap_handle_t *dhp = (devmap_handle_t *)dhc;
3479*0Sstevel@tonic-gate 	ddi_acc_handle_t handle;
3480*0Sstevel@tonic-gate 	ddi_map_req_t mr;
3481*0Sstevel@tonic-gate 	ddi_acc_hdl_t *hp;
3482*0Sstevel@tonic-gate 	pfn_t	pfn;
3483*0Sstevel@tonic-gate 	uint_t	hat_flags;
3484*0Sstevel@tonic-gate 	int	err;
3485*0Sstevel@tonic-gate 
3486*0Sstevel@tonic-gate 	TRACE_4(TR_FAC_DEVMAP, TR_DEVMAP_DEVMEM_REMAP,
3487*0Sstevel@tonic-gate 	    "devmap_devmem_setup:start dhp=%p offset=%llx rnum=%d len=%lx",
3488*0Sstevel@tonic-gate 	    (void *)dhp, roff, rnumber, (uint_t)len);
3489*0Sstevel@tonic-gate 	DEBUGF(2, (CE_CONT, "devmap_devmem_remap: dhp %p offset %llx "
3490*0Sstevel@tonic-gate 	    "rnum %d len %lx\n", (void *)dhp, roff, rnumber, len));
3491*0Sstevel@tonic-gate 
3492*0Sstevel@tonic-gate 	/*
3493*0Sstevel@tonic-gate 	 * Return failure if setup has not been done or no remap permission
3494*0Sstevel@tonic-gate 	 * has been granted during the setup.
3495*0Sstevel@tonic-gate 	 */
3496*0Sstevel@tonic-gate 	if ((dhp->dh_flags & DEVMAP_SETUP_DONE) == 0 ||
3497*0Sstevel@tonic-gate 	    (dhp->dh_flags & DEVMAP_ALLOW_REMAP) == 0)
3498*0Sstevel@tonic-gate 		return (DDI_FAILURE);
3499*0Sstevel@tonic-gate 
3500*0Sstevel@tonic-gate 	/* Only DEVMAP_MAPPING_INVALID flag supported for remap */
3501*0Sstevel@tonic-gate 	if ((flags != 0) && (flags != DEVMAP_MAPPING_INVALID))
3502*0Sstevel@tonic-gate 		return (DDI_FAILURE);
3503*0Sstevel@tonic-gate 
3504*0Sstevel@tonic-gate 	if ((dhp->dh_prot & dhp->dh_orig_maxprot & maxprot) != dhp->dh_prot)
3505*0Sstevel@tonic-gate 		return (DDI_FAILURE);
3506*0Sstevel@tonic-gate 
3507*0Sstevel@tonic-gate 	if (!(flags & DEVMAP_MAPPING_INVALID)) {
3508*0Sstevel@tonic-gate 		handle = impl_acc_hdl_alloc(KM_SLEEP, NULL);
3509*0Sstevel@tonic-gate 		if (handle == NULL)
3510*0Sstevel@tonic-gate 			return (DDI_FAILURE);
3511*0Sstevel@tonic-gate 	}
3512*0Sstevel@tonic-gate 
3513*0Sstevel@tonic-gate 	HOLD_DHP_LOCK(dhp);
3514*0Sstevel@tonic-gate 
3515*0Sstevel@tonic-gate 	/*
3516*0Sstevel@tonic-gate 	 * Unload the old mapping, so next fault will setup the new mappings
3517*0Sstevel@tonic-gate 	 * Do this while holding the dhp lock so other faults dont reestablish
3518*0Sstevel@tonic-gate 	 * the mappings
3519*0Sstevel@tonic-gate 	 */
3520*0Sstevel@tonic-gate 	hat_unload(dhp->dh_seg->s_as->a_hat, dhp->dh_uvaddr,
3521*0Sstevel@tonic-gate 		dhp->dh_len, HAT_UNLOAD|HAT_UNLOAD_OTHER);
3522*0Sstevel@tonic-gate 
3523*0Sstevel@tonic-gate 	if (flags & DEVMAP_MAPPING_INVALID) {
3524*0Sstevel@tonic-gate 		dhp->dh_flags |= DEVMAP_MAPPING_INVALID;
3525*0Sstevel@tonic-gate 		dhp->dh_pfn = PFN_INVALID;
3526*0Sstevel@tonic-gate 	} else {
3527*0Sstevel@tonic-gate 		/* clear any prior DEVMAP_MAPPING_INVALID flag */
3528*0Sstevel@tonic-gate 		dhp->dh_flags &= ~DEVMAP_MAPPING_INVALID;
3529*0Sstevel@tonic-gate 		hp = impl_acc_hdl_get(handle);
3530*0Sstevel@tonic-gate 		hp->ah_vers = VERS_ACCHDL;
3531*0Sstevel@tonic-gate 		hp->ah_dip = dip;
3532*0Sstevel@tonic-gate 		hp->ah_rnumber = rnumber;
3533*0Sstevel@tonic-gate 		hp->ah_offset = roff;
3534*0Sstevel@tonic-gate 		hp->ah_len = len;
3535*0Sstevel@tonic-gate 		if (accattrp != NULL)
3536*0Sstevel@tonic-gate 			hp->ah_acc = *accattrp;
3537*0Sstevel@tonic-gate 
3538*0Sstevel@tonic-gate 		mr.map_op = DDI_MO_MAP_LOCKED;
3539*0Sstevel@tonic-gate 		mr.map_type = DDI_MT_RNUMBER;
3540*0Sstevel@tonic-gate 		mr.map_obj.rnumber = rnumber;
3541*0Sstevel@tonic-gate 		mr.map_prot = maxprot & dhp->dh_orig_maxprot;
3542*0Sstevel@tonic-gate 		mr.map_flags = DDI_MF_DEVICE_MAPPING;
3543*0Sstevel@tonic-gate 		mr.map_handlep = hp;
3544*0Sstevel@tonic-gate 		mr.map_vers = DDI_MAP_VERSION;
3545*0Sstevel@tonic-gate 
3546*0Sstevel@tonic-gate 		/*
3547*0Sstevel@tonic-gate 		 * up the device tree to get pfn.
3548*0Sstevel@tonic-gate 		 * The rootnex_map_regspec() routine in nexus drivers has been
3549*0Sstevel@tonic-gate 		 * modified to return pfn if map_flags is DDI_MF_DEVICE_MAPPING.
3550*0Sstevel@tonic-gate 		 */
3551*0Sstevel@tonic-gate 		err = ddi_map(dip, &mr, roff, len, (caddr_t *)&pfn);
3552*0Sstevel@tonic-gate 		hat_flags = hp->ah_hat_flags;
3553*0Sstevel@tonic-gate 		impl_acc_hdl_free(handle);
3554*0Sstevel@tonic-gate 		if (err) {
3555*0Sstevel@tonic-gate 			RELE_DHP_LOCK(dhp);
3556*0Sstevel@tonic-gate 			return (DDI_FAILURE);
3557*0Sstevel@tonic-gate 		}
3558*0Sstevel@tonic-gate 		/*
3559*0Sstevel@tonic-gate 		 * Store result of ddi_map first in local variables, as we do
3560*0Sstevel@tonic-gate 		 * not want to overwrite the existing dhp with wrong data.
3561*0Sstevel@tonic-gate 		 */
3562*0Sstevel@tonic-gate 		dhp->dh_pfn = pfn;
3563*0Sstevel@tonic-gate 		dhp->dh_hat_attr = hat_flags;
3564*0Sstevel@tonic-gate 	}
3565*0Sstevel@tonic-gate 
3566*0Sstevel@tonic-gate 	/* clear the large page size flag */
3567*0Sstevel@tonic-gate 	dhp->dh_flags &= ~DEVMAP_FLAG_LARGE;
3568*0Sstevel@tonic-gate 
3569*0Sstevel@tonic-gate 	dhp->dh_cookie = DEVMAP_DEVMEM_COOKIE;
3570*0Sstevel@tonic-gate 	dhp->dh_roff = ptob(btop(roff));
3571*0Sstevel@tonic-gate 
3572*0Sstevel@tonic-gate 	/* setup the dh_mmulevel and DEVMAP_FLAG_LARGE */
3573*0Sstevel@tonic-gate 	devmap_devmem_large_page_setup(dhp);
3574*0Sstevel@tonic-gate 	dhp->dh_maxprot = maxprot & dhp->dh_orig_maxprot;
3575*0Sstevel@tonic-gate 	ASSERT((dhp->dh_prot & dhp->dh_orig_maxprot & maxprot) == dhp->dh_prot);
3576*0Sstevel@tonic-gate 
3577*0Sstevel@tonic-gate 	RELE_DHP_LOCK(dhp);
3578*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
3579*0Sstevel@tonic-gate }
3580*0Sstevel@tonic-gate 
3581*0Sstevel@tonic-gate /*
3582*0Sstevel@tonic-gate  * called by driver devmap routine to pass kernel virtual address  mapping
3583*0Sstevel@tonic-gate  * info to the framework.    used only for kernel memory
3584*0Sstevel@tonic-gate  * allocated from ddi_umem_alloc().
3585*0Sstevel@tonic-gate  */
3586*0Sstevel@tonic-gate int
3587*0Sstevel@tonic-gate devmap_umem_setup(devmap_cookie_t dhc, dev_info_t *dip,
3588*0Sstevel@tonic-gate     struct devmap_callback_ctl *callbackops, ddi_umem_cookie_t cookie,
3589*0Sstevel@tonic-gate     offset_t off, size_t len, uint_t maxprot, uint_t flags,
3590*0Sstevel@tonic-gate     ddi_device_acc_attr_t *accattrp)
3591*0Sstevel@tonic-gate {
3592*0Sstevel@tonic-gate 	devmap_handle_t *dhp = (devmap_handle_t *)dhc;
3593*0Sstevel@tonic-gate 	struct ddi_umem_cookie *cp = (struct ddi_umem_cookie *)cookie;
3594*0Sstevel@tonic-gate 
3595*0Sstevel@tonic-gate #ifdef lint
3596*0Sstevel@tonic-gate 	dip = dip;
3597*0Sstevel@tonic-gate 	accattrp = accattrp;
3598*0Sstevel@tonic-gate #endif
3599*0Sstevel@tonic-gate 
3600*0Sstevel@tonic-gate 	TRACE_4(TR_FAC_DEVMAP, TR_DEVMAP_UMEM_SETUP,
3601*0Sstevel@tonic-gate 	    "devmap_umem_setup:start dhp=%p offset=%llx cookie=%p len=%lx",
3602*0Sstevel@tonic-gate 	    (void *)dhp, off, cookie, len);
3603*0Sstevel@tonic-gate 	DEBUGF(2, (CE_CONT, "devmap_umem_setup: dhp %p offset %llx "
3604*0Sstevel@tonic-gate 	    "cookie %p len %lx\n", (void *)dhp, off, (void *)cookie, len));
3605*0Sstevel@tonic-gate 
3606*0Sstevel@tonic-gate 	if (cookie == NULL)
3607*0Sstevel@tonic-gate 		return (DDI_FAILURE);
3608*0Sstevel@tonic-gate 
3609*0Sstevel@tonic-gate 	/* For UMEM_TRASH, this restriction is not needed */
3610*0Sstevel@tonic-gate 	if ((off + len) > cp->size)
3611*0Sstevel@tonic-gate 		return (DDI_FAILURE);
3612*0Sstevel@tonic-gate 
3613*0Sstevel@tonic-gate 	/*
3614*0Sstevel@tonic-gate 	 * First to check if this function has been called for this dhp.
3615*0Sstevel@tonic-gate 	 */
3616*0Sstevel@tonic-gate 	if (dhp->dh_flags & DEVMAP_SETUP_DONE)
3617*0Sstevel@tonic-gate 		return (DDI_FAILURE);
3618*0Sstevel@tonic-gate 
3619*0Sstevel@tonic-gate 	if ((dhp->dh_prot & dhp->dh_orig_maxprot & maxprot) != dhp->dh_prot)
3620*0Sstevel@tonic-gate 		return (DDI_FAILURE);
3621*0Sstevel@tonic-gate 
3622*0Sstevel@tonic-gate 	if (flags & DEVMAP_MAPPING_INVALID) {
3623*0Sstevel@tonic-gate 		/*
3624*0Sstevel@tonic-gate 		 * If DEVMAP_MAPPING_INVALID is specified, we have to grant
3625*0Sstevel@tonic-gate 		 * remap permission.
3626*0Sstevel@tonic-gate 		 */
3627*0Sstevel@tonic-gate 		if (!(flags & DEVMAP_ALLOW_REMAP)) {
3628*0Sstevel@tonic-gate 			return (DDI_FAILURE);
3629*0Sstevel@tonic-gate 		}
3630*0Sstevel@tonic-gate 	} else {
3631*0Sstevel@tonic-gate 		dhp->dh_cookie = cookie;
3632*0Sstevel@tonic-gate 		dhp->dh_roff = ptob(btop(off));
3633*0Sstevel@tonic-gate 		dhp->dh_cvaddr = cp->cvaddr + dhp->dh_roff;
3634*0Sstevel@tonic-gate 	}
3635*0Sstevel@tonic-gate 
3636*0Sstevel@tonic-gate 	/*
3637*0Sstevel@tonic-gate 	 * The default is _not_ to pass HAT_LOAD_NOCONSIST to hat_devload();
3638*0Sstevel@tonic-gate 	 * we pass HAT_LOAD_NOCONSIST _only_ in cases where hat tries to
3639*0Sstevel@tonic-gate 	 * create consistent mappings but our intention was to create
3640*0Sstevel@tonic-gate 	 * non-consistent mappings.
3641*0Sstevel@tonic-gate 	 *
3642*0Sstevel@tonic-gate 	 * DEVMEM: hat figures it out it's DEVMEM and creates non-consistent
3643*0Sstevel@tonic-gate 	 * mappings.
3644*0Sstevel@tonic-gate 	 *
3645*0Sstevel@tonic-gate 	 * kernel exported memory: hat figures it out it's memory and always
3646*0Sstevel@tonic-gate 	 * creates consistent mappings.
3647*0Sstevel@tonic-gate 	 *
3648*0Sstevel@tonic-gate 	 * /dev/mem: non-consistent mappings. See comments in common/io/mem.c
3649*0Sstevel@tonic-gate 	 *
3650*0Sstevel@tonic-gate 	 * /dev/kmem: consistent mappings are created unless they are
3651*0Sstevel@tonic-gate 	 * MAP_FIXED. We _explicitly_ tell hat to create non-consistent
3652*0Sstevel@tonic-gate 	 * mappings by passing HAT_LOAD_NOCONSIST in case of MAP_FIXED
3653*0Sstevel@tonic-gate 	 * mappings of /dev/kmem. See common/io/mem.c
3654*0Sstevel@tonic-gate 	 */
3655*0Sstevel@tonic-gate 
3656*0Sstevel@tonic-gate 	/* Only some of the flags bits are settable by the driver */
3657*0Sstevel@tonic-gate 	dhp->dh_flags |= (flags & DEVMAP_SETUP_FLAGS);
3658*0Sstevel@tonic-gate 
3659*0Sstevel@tonic-gate 	dhp->dh_len = ptob(btopr(len));
3660*0Sstevel@tonic-gate 	dhp->dh_maxprot = maxprot & dhp->dh_orig_maxprot;
3661*0Sstevel@tonic-gate 	ASSERT((dhp->dh_prot & dhp->dh_orig_maxprot & maxprot) == dhp->dh_prot);
3662*0Sstevel@tonic-gate 
3663*0Sstevel@tonic-gate 	if (callbackops != NULL) {
3664*0Sstevel@tonic-gate 		bcopy(callbackops, &dhp->dh_callbackops,
3665*0Sstevel@tonic-gate 		    sizeof (struct devmap_callback_ctl));
3666*0Sstevel@tonic-gate 	}
3667*0Sstevel@tonic-gate 	/*
3668*0Sstevel@tonic-gate 	 * Initialize dh_lock if we want to do remap.
3669*0Sstevel@tonic-gate 	 */
3670*0Sstevel@tonic-gate 	if (dhp->dh_flags & DEVMAP_ALLOW_REMAP) {
3671*0Sstevel@tonic-gate 		mutex_init(&dhp->dh_lock, NULL, MUTEX_DEFAULT, NULL);
3672*0Sstevel@tonic-gate 		dhp->dh_flags |= DEVMAP_LOCK_INITED;
3673*0Sstevel@tonic-gate 	}
3674*0Sstevel@tonic-gate 
3675*0Sstevel@tonic-gate 	dhp->dh_flags |= DEVMAP_SETUP_DONE;
3676*0Sstevel@tonic-gate 
3677*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
3678*0Sstevel@tonic-gate }
3679*0Sstevel@tonic-gate 
3680*0Sstevel@tonic-gate int
3681*0Sstevel@tonic-gate devmap_umem_remap(devmap_cookie_t dhc, dev_info_t *dip,
3682*0Sstevel@tonic-gate     ddi_umem_cookie_t cookie, offset_t off, size_t len, uint_t maxprot,
3683*0Sstevel@tonic-gate     uint_t flags, ddi_device_acc_attr_t *accattrp)
3684*0Sstevel@tonic-gate {
3685*0Sstevel@tonic-gate 	devmap_handle_t *dhp = (devmap_handle_t *)dhc;
3686*0Sstevel@tonic-gate 	struct ddi_umem_cookie *cp = (struct ddi_umem_cookie *)cookie;
3687*0Sstevel@tonic-gate 
3688*0Sstevel@tonic-gate 	TRACE_4(TR_FAC_DEVMAP, TR_DEVMAP_UMEM_REMAP,
3689*0Sstevel@tonic-gate 	    "devmap_umem_remap:start dhp=%p offset=%llx cookie=%p len=%lx",
3690*0Sstevel@tonic-gate 	    (void *)dhp, off, cookie, len);
3691*0Sstevel@tonic-gate 	DEBUGF(2, (CE_CONT, "devmap_umem_remap: dhp %p offset %llx "
3692*0Sstevel@tonic-gate 	    "cookie %p len %lx\n", (void *)dhp, off, (void *)cookie, len));
3693*0Sstevel@tonic-gate 
3694*0Sstevel@tonic-gate #ifdef lint
3695*0Sstevel@tonic-gate 	dip = dip;
3696*0Sstevel@tonic-gate 	accattrp = accattrp;
3697*0Sstevel@tonic-gate #endif
3698*0Sstevel@tonic-gate 	/*
3699*0Sstevel@tonic-gate 	 * Reture failure if setup has not been done or no remap permission
3700*0Sstevel@tonic-gate 	 * has been granted during the setup.
3701*0Sstevel@tonic-gate 	 */
3702*0Sstevel@tonic-gate 	if ((dhp->dh_flags & DEVMAP_SETUP_DONE) == 0 ||
3703*0Sstevel@tonic-gate 		(dhp->dh_flags & DEVMAP_ALLOW_REMAP) == 0)
3704*0Sstevel@tonic-gate 		return (DDI_FAILURE);
3705*0Sstevel@tonic-gate 
3706*0Sstevel@tonic-gate 	/* No flags supported for remap yet */
3707*0Sstevel@tonic-gate 	if (flags != 0)
3708*0Sstevel@tonic-gate 		return (DDI_FAILURE);
3709*0Sstevel@tonic-gate 
3710*0Sstevel@tonic-gate 	if ((dhp->dh_prot & dhp->dh_orig_maxprot & maxprot) != dhp->dh_prot)
3711*0Sstevel@tonic-gate 		return (DDI_FAILURE);
3712*0Sstevel@tonic-gate 
3713*0Sstevel@tonic-gate 	/* For UMEM_TRASH, this restriction is not needed */
3714*0Sstevel@tonic-gate 	if ((off + len) > cp->size)
3715*0Sstevel@tonic-gate 		return (DDI_FAILURE);
3716*0Sstevel@tonic-gate 
3717*0Sstevel@tonic-gate 	HOLD_DHP_LOCK(dhp);
3718*0Sstevel@tonic-gate 	/*
3719*0Sstevel@tonic-gate 	 * Unload the old mapping, so next fault will setup the new mappings
3720*0Sstevel@tonic-gate 	 * Do this while holding the dhp lock so other faults dont reestablish
3721*0Sstevel@tonic-gate 	 * the mappings
3722*0Sstevel@tonic-gate 	 */
3723*0Sstevel@tonic-gate 	hat_unload(dhp->dh_seg->s_as->a_hat, dhp->dh_uvaddr,
3724*0Sstevel@tonic-gate 		dhp->dh_len, HAT_UNLOAD|HAT_UNLOAD_OTHER);
3725*0Sstevel@tonic-gate 
3726*0Sstevel@tonic-gate 	dhp->dh_cookie = cookie;
3727*0Sstevel@tonic-gate 	dhp->dh_roff = ptob(btop(off));
3728*0Sstevel@tonic-gate 	dhp->dh_cvaddr = cp->cvaddr + dhp->dh_roff;
3729*0Sstevel@tonic-gate 
3730*0Sstevel@tonic-gate 	/* clear the large page size flag */
3731*0Sstevel@tonic-gate 	dhp->dh_flags &= ~DEVMAP_FLAG_LARGE;
3732*0Sstevel@tonic-gate 
3733*0Sstevel@tonic-gate 	dhp->dh_maxprot = maxprot & dhp->dh_orig_maxprot;
3734*0Sstevel@tonic-gate 	ASSERT((dhp->dh_prot & dhp->dh_orig_maxprot & maxprot) == dhp->dh_prot);
3735*0Sstevel@tonic-gate 	RELE_DHP_LOCK(dhp);
3736*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
3737*0Sstevel@tonic-gate }
3738*0Sstevel@tonic-gate 
3739*0Sstevel@tonic-gate /*
3740*0Sstevel@tonic-gate  * to set timeout value for the driver's context management callback, e.g.
3741*0Sstevel@tonic-gate  * devmap_access().
3742*0Sstevel@tonic-gate  */
3743*0Sstevel@tonic-gate void
3744*0Sstevel@tonic-gate devmap_set_ctx_timeout(devmap_cookie_t dhc, clock_t ticks)
3745*0Sstevel@tonic-gate {
3746*0Sstevel@tonic-gate 	devmap_handle_t *dhp = (devmap_handle_t *)dhc;
3747*0Sstevel@tonic-gate 
3748*0Sstevel@tonic-gate 	TRACE_2(TR_FAC_DEVMAP, TR_DEVMAP_SET_CTX_TIMEOUT,
3749*0Sstevel@tonic-gate 	    "devmap_set_ctx_timeout:start dhp=%p ticks=%x",
3750*0Sstevel@tonic-gate 	    (void *)dhp, ticks);
3751*0Sstevel@tonic-gate 	dhp->dh_timeout_length = ticks;
3752*0Sstevel@tonic-gate }
3753*0Sstevel@tonic-gate 
3754*0Sstevel@tonic-gate int
3755*0Sstevel@tonic-gate devmap_default_access(devmap_cookie_t dhp, void *pvtp, offset_t off,
3756*0Sstevel@tonic-gate     size_t len, uint_t type, uint_t rw)
3757*0Sstevel@tonic-gate {
3758*0Sstevel@tonic-gate #ifdef lint
3759*0Sstevel@tonic-gate 	pvtp = pvtp;
3760*0Sstevel@tonic-gate #endif
3761*0Sstevel@tonic-gate 
3762*0Sstevel@tonic-gate 	TRACE_0(TR_FAC_DEVMAP, TR_DEVMAP_DEFAULT_ACCESS,
3763*0Sstevel@tonic-gate 	    "devmap_default_access:start");
3764*0Sstevel@tonic-gate 	return (devmap_load(dhp, off, len, type, rw));
3765*0Sstevel@tonic-gate }
3766*0Sstevel@tonic-gate 
3767*0Sstevel@tonic-gate /*
3768*0Sstevel@tonic-gate  * segkmem_alloc() wrapper to allocate memory which is both
3769*0Sstevel@tonic-gate  * non-relocatable (for DR) and sharelocked, since the rest
3770*0Sstevel@tonic-gate  * of this segment driver requires it.
3771*0Sstevel@tonic-gate  */
3772*0Sstevel@tonic-gate static void *
3773*0Sstevel@tonic-gate devmap_alloc_pages(vmem_t *vmp, size_t size, int vmflag)
3774*0Sstevel@tonic-gate {
3775*0Sstevel@tonic-gate 	ASSERT(vmp != NULL);
3776*0Sstevel@tonic-gate 	ASSERT(kvseg.s_base != NULL);
3777*0Sstevel@tonic-gate 	vmflag |= (VM_NORELOC | SEGKMEM_SHARELOCKED);
3778*0Sstevel@tonic-gate 	return (segkmem_alloc(vmp, size, vmflag));
3779*0Sstevel@tonic-gate }
3780*0Sstevel@tonic-gate 
3781*0Sstevel@tonic-gate /*
3782*0Sstevel@tonic-gate  * This is where things are a bit incestrous with seg_kmem: unlike
3783*0Sstevel@tonic-gate  * seg_kp, seg_kmem does not keep its pages long-term sharelocked, so
3784*0Sstevel@tonic-gate  * we need to do a bit of a dance around that to prevent duplication of
3785*0Sstevel@tonic-gate  * code until we decide to bite the bullet and implement a new kernel
3786*0Sstevel@tonic-gate  * segment for driver-allocated memory that is exported to user space.
3787*0Sstevel@tonic-gate  */
3788*0Sstevel@tonic-gate static void
3789*0Sstevel@tonic-gate devmap_free_pages(vmem_t *vmp, void *inaddr, size_t size)
3790*0Sstevel@tonic-gate {
3791*0Sstevel@tonic-gate 	page_t *pp;
3792*0Sstevel@tonic-gate 	caddr_t addr = inaddr;
3793*0Sstevel@tonic-gate 	caddr_t eaddr;
3794*0Sstevel@tonic-gate 	pgcnt_t npages = btopr(size);
3795*0Sstevel@tonic-gate 
3796*0Sstevel@tonic-gate 	ASSERT(vmp != NULL);
3797*0Sstevel@tonic-gate 	ASSERT(kvseg.s_base != NULL);
3798*0Sstevel@tonic-gate 	ASSERT(((uintptr_t)addr & PAGEOFFSET) == 0);
3799*0Sstevel@tonic-gate 
3800*0Sstevel@tonic-gate 	hat_unload(kas.a_hat, addr, size, HAT_UNLOAD_UNLOCK);
3801*0Sstevel@tonic-gate 
3802*0Sstevel@tonic-gate 	for (eaddr = addr + size; addr < eaddr; addr += PAGESIZE) {
3803*0Sstevel@tonic-gate 		/*
3804*0Sstevel@tonic-gate 		 * Use page_find() instead of page_lookup() to find the page
3805*0Sstevel@tonic-gate 		 * since we know that it is hashed and has a shared lock.
3806*0Sstevel@tonic-gate 		 */
3807*0Sstevel@tonic-gate 		pp = page_find(&kvp, (u_offset_t)(uintptr_t)addr);
3808*0Sstevel@tonic-gate 
3809*0Sstevel@tonic-gate 		if (pp == NULL)
3810*0Sstevel@tonic-gate 			panic("devmap_free_pages: page not found");
3811*0Sstevel@tonic-gate 		if (!page_tryupgrade(pp)) {
3812*0Sstevel@tonic-gate 			page_unlock(pp);
3813*0Sstevel@tonic-gate 			pp = page_lookup(&kvp, (u_offset_t)(uintptr_t)addr,
3814*0Sstevel@tonic-gate 			    SE_EXCL);
3815*0Sstevel@tonic-gate 			if (pp == NULL)
3816*0Sstevel@tonic-gate 				panic("devmap_free_pages: page already freed");
3817*0Sstevel@tonic-gate 		}
3818*0Sstevel@tonic-gate 		/* Clear p_lckcnt so page_destroy() doesn't update availrmem */
3819*0Sstevel@tonic-gate 		pp->p_lckcnt = 0;
3820*0Sstevel@tonic-gate 		page_destroy(pp, 0);
3821*0Sstevel@tonic-gate 	}
3822*0Sstevel@tonic-gate 	page_unresv(npages);
3823*0Sstevel@tonic-gate 
3824*0Sstevel@tonic-gate 	if (vmp != NULL)
3825*0Sstevel@tonic-gate 		vmem_free(vmp, inaddr, size);
3826*0Sstevel@tonic-gate }
3827*0Sstevel@tonic-gate 
3828*0Sstevel@tonic-gate /*
3829*0Sstevel@tonic-gate  * devmap_umem_alloc_np() replaces kmem_zalloc() as the method for
3830*0Sstevel@tonic-gate  * allocating non-pageable kmem in response to a ddi_umem_alloc()
3831*0Sstevel@tonic-gate  * default request. For now we allocate our own pages and we keep
3832*0Sstevel@tonic-gate  * them long-term sharelocked, since: A) the fault routines expect the
3833*0Sstevel@tonic-gate  * memory to already be locked; B) pageable umem is already long-term
3834*0Sstevel@tonic-gate  * locked; C) it's a lot of work to make it otherwise, particuarly
3835*0Sstevel@tonic-gate  * since the nexus layer expects the pages to never fault. An RFE is to
3836*0Sstevel@tonic-gate  * not keep the pages long-term locked, but instead to be able to
3837*0Sstevel@tonic-gate  * take faults on them and simply look them up in kvp in case we
3838*0Sstevel@tonic-gate  * fault on them. Even then, we must take care not to let pageout
3839*0Sstevel@tonic-gate  * steal them from us since the data must remain resident; if we
3840*0Sstevel@tonic-gate  * do this we must come up with some way to pin the pages to prevent
3841*0Sstevel@tonic-gate  * faults while a driver is doing DMA to/from them.
3842*0Sstevel@tonic-gate  */
3843*0Sstevel@tonic-gate static void *
3844*0Sstevel@tonic-gate devmap_umem_alloc_np(size_t size, size_t flags)
3845*0Sstevel@tonic-gate {
3846*0Sstevel@tonic-gate 	void *buf;
3847*0Sstevel@tonic-gate 	int vmflags = (flags & DDI_UMEM_NOSLEEP)? VM_NOSLEEP : VM_SLEEP;
3848*0Sstevel@tonic-gate 
3849*0Sstevel@tonic-gate 	buf = vmem_alloc(umem_np_arena, size, vmflags);
3850*0Sstevel@tonic-gate 	if (buf != NULL)
3851*0Sstevel@tonic-gate 		bzero(buf, size);
3852*0Sstevel@tonic-gate 	return (buf);
3853*0Sstevel@tonic-gate }
3854*0Sstevel@tonic-gate 
3855*0Sstevel@tonic-gate static void
3856*0Sstevel@tonic-gate devmap_umem_free_np(void *addr, size_t size)
3857*0Sstevel@tonic-gate {
3858*0Sstevel@tonic-gate 	vmem_free(umem_np_arena, addr, size);
3859*0Sstevel@tonic-gate }
3860*0Sstevel@tonic-gate 
3861*0Sstevel@tonic-gate /*
3862*0Sstevel@tonic-gate  * allocate page aligned kernel memory for exporting to user land.
3863*0Sstevel@tonic-gate  * The devmap framework will use the cookie allocated by ddi_umem_alloc()
3864*0Sstevel@tonic-gate  * to find a user virtual address that is in same color as the address
3865*0Sstevel@tonic-gate  * allocated here.
3866*0Sstevel@tonic-gate  */
3867*0Sstevel@tonic-gate void *
3868*0Sstevel@tonic-gate ddi_umem_alloc(size_t size, int flags, ddi_umem_cookie_t *cookie)
3869*0Sstevel@tonic-gate {
3870*0Sstevel@tonic-gate 	register size_t len = ptob(btopr(size));
3871*0Sstevel@tonic-gate 	void *buf = NULL;
3872*0Sstevel@tonic-gate 	struct ddi_umem_cookie *cp;
3873*0Sstevel@tonic-gate 	int iflags = 0;
3874*0Sstevel@tonic-gate 
3875*0Sstevel@tonic-gate 	*cookie = NULL;
3876*0Sstevel@tonic-gate 
3877*0Sstevel@tonic-gate 	TRACE_0(TR_FAC_DEVMAP, TR_DEVMAP_UMEM_ALLOC,
3878*0Sstevel@tonic-gate 	    "devmap_umem_alloc:start");
3879*0Sstevel@tonic-gate 	if (len == 0)
3880*0Sstevel@tonic-gate 		return ((void *)NULL);
3881*0Sstevel@tonic-gate 
3882*0Sstevel@tonic-gate 	/*
3883*0Sstevel@tonic-gate 	 * allocate cookie
3884*0Sstevel@tonic-gate 	 */
3885*0Sstevel@tonic-gate 	if ((cp = kmem_zalloc(sizeof (struct ddi_umem_cookie),
3886*0Sstevel@tonic-gate 		flags & DDI_UMEM_NOSLEEP ? KM_NOSLEEP : KM_SLEEP)) == NULL) {
3887*0Sstevel@tonic-gate 		ASSERT(flags & DDI_UMEM_NOSLEEP);
3888*0Sstevel@tonic-gate 		return ((void *)NULL);
3889*0Sstevel@tonic-gate 	}
3890*0Sstevel@tonic-gate 
3891*0Sstevel@tonic-gate 	if (flags & DDI_UMEM_PAGEABLE) {
3892*0Sstevel@tonic-gate 		/* Only one of the flags is allowed */
3893*0Sstevel@tonic-gate 		ASSERT(!(flags & DDI_UMEM_TRASH));
3894*0Sstevel@tonic-gate 		/* initialize resource with 0 */
3895*0Sstevel@tonic-gate 		iflags = KPD_ZERO;
3896*0Sstevel@tonic-gate 
3897*0Sstevel@tonic-gate 		/*
3898*0Sstevel@tonic-gate 		 * to allocate unlocked pageable memory, use segkp_get() to
3899*0Sstevel@tonic-gate 		 * create a segkp segment.  Since segkp can only service kas,
3900*0Sstevel@tonic-gate 		 * other segment drivers such as segdev have to do
3901*0Sstevel@tonic-gate 		 * as_fault(segkp, SOFTLOCK) in its fault routine,
3902*0Sstevel@tonic-gate 		 */
3903*0Sstevel@tonic-gate 		if (flags & DDI_UMEM_NOSLEEP)
3904*0Sstevel@tonic-gate 			iflags |= KPD_NOWAIT;
3905*0Sstevel@tonic-gate 
3906*0Sstevel@tonic-gate 		if ((buf = segkp_get(segkp, len, iflags)) == NULL) {
3907*0Sstevel@tonic-gate 			kmem_free(cp, sizeof (struct ddi_umem_cookie));
3908*0Sstevel@tonic-gate 			return ((void *)NULL);
3909*0Sstevel@tonic-gate 		}
3910*0Sstevel@tonic-gate 		cp->type = KMEM_PAGEABLE;
3911*0Sstevel@tonic-gate 		mutex_init(&cp->lock, NULL, MUTEX_DEFAULT, NULL);
3912*0Sstevel@tonic-gate 		cp->locked = 0;
3913*0Sstevel@tonic-gate 	} else if (flags & DDI_UMEM_TRASH) {
3914*0Sstevel@tonic-gate 		/* Only one of the flags is allowed */
3915*0Sstevel@tonic-gate 		ASSERT(!(flags & DDI_UMEM_PAGEABLE));
3916*0Sstevel@tonic-gate 		cp->type = UMEM_TRASH;
3917*0Sstevel@tonic-gate 		buf = NULL;
3918*0Sstevel@tonic-gate 	} else {
3919*0Sstevel@tonic-gate 		if ((buf = devmap_umem_alloc_np(len, flags)) == NULL) {
3920*0Sstevel@tonic-gate 			kmem_free(cp, sizeof (struct ddi_umem_cookie));
3921*0Sstevel@tonic-gate 			return ((void *)NULL);
3922*0Sstevel@tonic-gate 		}
3923*0Sstevel@tonic-gate 
3924*0Sstevel@tonic-gate 		cp->type = KMEM_NON_PAGEABLE;
3925*0Sstevel@tonic-gate 	}
3926*0Sstevel@tonic-gate 
3927*0Sstevel@tonic-gate 	/*
3928*0Sstevel@tonic-gate 	 * need to save size here.  size will be used when
3929*0Sstevel@tonic-gate 	 * we do kmem_free.
3930*0Sstevel@tonic-gate 	 */
3931*0Sstevel@tonic-gate 	cp->size = len;
3932*0Sstevel@tonic-gate 	cp->cvaddr = (caddr_t)buf;
3933*0Sstevel@tonic-gate 
3934*0Sstevel@tonic-gate 	*cookie =  (void *)cp;
3935*0Sstevel@tonic-gate 	return (buf);
3936*0Sstevel@tonic-gate }
3937*0Sstevel@tonic-gate 
3938*0Sstevel@tonic-gate void
3939*0Sstevel@tonic-gate ddi_umem_free(ddi_umem_cookie_t cookie)
3940*0Sstevel@tonic-gate {
3941*0Sstevel@tonic-gate 	struct ddi_umem_cookie *cp;
3942*0Sstevel@tonic-gate 
3943*0Sstevel@tonic-gate 	TRACE_0(TR_FAC_DEVMAP, TR_DEVMAP_UMEM_FREE,
3944*0Sstevel@tonic-gate 	    "devmap_umem_free:start");
3945*0Sstevel@tonic-gate 
3946*0Sstevel@tonic-gate 	/*
3947*0Sstevel@tonic-gate 	 * if cookie is NULL, no effects on the system
3948*0Sstevel@tonic-gate 	 */
3949*0Sstevel@tonic-gate 	if (cookie == NULL)
3950*0Sstevel@tonic-gate 		return;
3951*0Sstevel@tonic-gate 
3952*0Sstevel@tonic-gate 	cp = (struct ddi_umem_cookie *)cookie;
3953*0Sstevel@tonic-gate 
3954*0Sstevel@tonic-gate 	switch (cp->type) {
3955*0Sstevel@tonic-gate 	case KMEM_PAGEABLE :
3956*0Sstevel@tonic-gate 		ASSERT(cp->cvaddr != NULL && cp->size != 0);
3957*0Sstevel@tonic-gate 		/*
3958*0Sstevel@tonic-gate 		 * Check if there are still any pending faults on the cookie
3959*0Sstevel@tonic-gate 		 * while the driver is deleting it,
3960*0Sstevel@tonic-gate 		 * XXX - could change to an ASSERT but wont catch errant drivers
3961*0Sstevel@tonic-gate 		 */
3962*0Sstevel@tonic-gate 		mutex_enter(&cp->lock);
3963*0Sstevel@tonic-gate 		if (cp->locked) {
3964*0Sstevel@tonic-gate 			mutex_exit(&cp->lock);
3965*0Sstevel@tonic-gate 			panic("ddi_umem_free for cookie with pending faults %p",
3966*0Sstevel@tonic-gate 			    (void *)cp);
3967*0Sstevel@tonic-gate 			return;
3968*0Sstevel@tonic-gate 		}
3969*0Sstevel@tonic-gate 
3970*0Sstevel@tonic-gate 		segkp_release(segkp, cp->cvaddr);
3971*0Sstevel@tonic-gate 
3972*0Sstevel@tonic-gate 		/*
3973*0Sstevel@tonic-gate 		 * release mutex associated with this cookie.
3974*0Sstevel@tonic-gate 		 */
3975*0Sstevel@tonic-gate 		mutex_destroy(&cp->lock);
3976*0Sstevel@tonic-gate 		break;
3977*0Sstevel@tonic-gate 	case KMEM_NON_PAGEABLE :
3978*0Sstevel@tonic-gate 		ASSERT(cp->cvaddr != NULL && cp->size != 0);
3979*0Sstevel@tonic-gate 		devmap_umem_free_np(cp->cvaddr, cp->size);
3980*0Sstevel@tonic-gate 		break;
3981*0Sstevel@tonic-gate 	case UMEM_TRASH :
3982*0Sstevel@tonic-gate 		break;
3983*0Sstevel@tonic-gate 	case UMEM_LOCKED :
3984*0Sstevel@tonic-gate 		/* Callers should use ddi_umem_unlock for this type */
3985*0Sstevel@tonic-gate 		ddi_umem_unlock(cookie);
3986*0Sstevel@tonic-gate 		/* Frees the cookie too */
3987*0Sstevel@tonic-gate 		return;
3988*0Sstevel@tonic-gate 	default:
3989*0Sstevel@tonic-gate 		/* panic so we can diagnose the underlying cause */
3990*0Sstevel@tonic-gate 		panic("ddi_umem_free: illegal cookie type 0x%x\n",
3991*0Sstevel@tonic-gate 		    cp->type);
3992*0Sstevel@tonic-gate 	}
3993*0Sstevel@tonic-gate 
3994*0Sstevel@tonic-gate 	kmem_free(cookie, sizeof (struct ddi_umem_cookie));
3995*0Sstevel@tonic-gate }
3996*0Sstevel@tonic-gate 
3997*0Sstevel@tonic-gate 
3998*0Sstevel@tonic-gate static int
3999*0Sstevel@tonic-gate segdev_getmemid(struct seg *seg, caddr_t addr, memid_t *memidp)
4000*0Sstevel@tonic-gate {
4001*0Sstevel@tonic-gate 	struct segdev_data *sdp = (struct segdev_data *)seg->s_data;
4002*0Sstevel@tonic-gate 
4003*0Sstevel@tonic-gate 	/*
4004*0Sstevel@tonic-gate 	 * It looks as if it is always mapped shared
4005*0Sstevel@tonic-gate 	 */
4006*0Sstevel@tonic-gate 	TRACE_0(TR_FAC_DEVMAP, TR_DEVMAP_GETMEMID,
4007*0Sstevel@tonic-gate 	    "segdev_getmemid:start");
4008*0Sstevel@tonic-gate 	memidp->val[0] = (uintptr_t)VTOCVP(sdp->vp);
4009*0Sstevel@tonic-gate 	memidp->val[1] = sdp->offset + (uintptr_t)(addr - seg->s_base);
4010*0Sstevel@tonic-gate 	return (0);
4011*0Sstevel@tonic-gate }
4012*0Sstevel@tonic-gate 
4013*0Sstevel@tonic-gate /*ARGSUSED*/
4014*0Sstevel@tonic-gate static lgrp_mem_policy_info_t *
4015*0Sstevel@tonic-gate segdev_getpolicy(struct seg *seg, caddr_t addr)
4016*0Sstevel@tonic-gate {
4017*0Sstevel@tonic-gate 	return (NULL);
4018*0Sstevel@tonic-gate }
4019*0Sstevel@tonic-gate 
4020*0Sstevel@tonic-gate /*
4021*0Sstevel@tonic-gate  * ddi_umem_alloc() non-pageable quantum cache max size.
4022*0Sstevel@tonic-gate  * This is just a SWAG.
4023*0Sstevel@tonic-gate  */
4024*0Sstevel@tonic-gate #define	DEVMAP_UMEM_QUANTUM	(8*PAGESIZE)
4025*0Sstevel@tonic-gate 
4026*0Sstevel@tonic-gate /*
4027*0Sstevel@tonic-gate  * Initialize seg_dev from boot. This routine sets up the trash page
4028*0Sstevel@tonic-gate  * and creates the umem_np_arena used to back non-pageable memory
4029*0Sstevel@tonic-gate  * requests.
4030*0Sstevel@tonic-gate  */
4031*0Sstevel@tonic-gate void
4032*0Sstevel@tonic-gate segdev_init(void)
4033*0Sstevel@tonic-gate {
4034*0Sstevel@tonic-gate 	struct seg kseg;
4035*0Sstevel@tonic-gate 
4036*0Sstevel@tonic-gate 	umem_np_arena = vmem_create("umem_np", NULL, 0, PAGESIZE,
4037*0Sstevel@tonic-gate 	    devmap_alloc_pages, devmap_free_pages, heap_arena,
4038*0Sstevel@tonic-gate 	    DEVMAP_UMEM_QUANTUM, VM_SLEEP);
4039*0Sstevel@tonic-gate 
4040*0Sstevel@tonic-gate 	kseg.s_as = &kas;
4041*0Sstevel@tonic-gate 	trashpp = page_create_va(&trashvp, 0, PAGESIZE,
4042*0Sstevel@tonic-gate 	    PG_NORELOC | PG_EXCL | PG_WAIT, &kseg, NULL);
4043*0Sstevel@tonic-gate 	if (trashpp == NULL)
4044*0Sstevel@tonic-gate 		panic("segdev_init: failed to create trash page");
4045*0Sstevel@tonic-gate 	pagezero(trashpp, 0, PAGESIZE);
4046*0Sstevel@tonic-gate 	page_downgrade(trashpp);
4047*0Sstevel@tonic-gate }
4048*0Sstevel@tonic-gate 
4049*0Sstevel@tonic-gate /*
4050*0Sstevel@tonic-gate  * Invoke platform-dependent support routines so that /proc can have
4051*0Sstevel@tonic-gate  * the platform code deal with curious hardware.
4052*0Sstevel@tonic-gate  */
4053*0Sstevel@tonic-gate int
4054*0Sstevel@tonic-gate segdev_copyfrom(struct seg *seg,
4055*0Sstevel@tonic-gate     caddr_t uaddr, const void *devaddr, void *kaddr, size_t len)
4056*0Sstevel@tonic-gate {
4057*0Sstevel@tonic-gate 	struct segdev_data *sdp = (struct segdev_data *)seg->s_data;
4058*0Sstevel@tonic-gate 	struct snode *sp = VTOS(VTOCVP(sdp->vp));
4059*0Sstevel@tonic-gate 
4060*0Sstevel@tonic-gate 	return (e_ddi_copyfromdev(sp->s_dip,
4061*0Sstevel@tonic-gate 	    (off_t)(uaddr - seg->s_base), devaddr, kaddr, len));
4062*0Sstevel@tonic-gate }
4063*0Sstevel@tonic-gate 
4064*0Sstevel@tonic-gate int
4065*0Sstevel@tonic-gate segdev_copyto(struct seg *seg,
4066*0Sstevel@tonic-gate     caddr_t uaddr, const void *kaddr, void *devaddr, size_t len)
4067*0Sstevel@tonic-gate {
4068*0Sstevel@tonic-gate 	struct segdev_data *sdp = (struct segdev_data *)seg->s_data;
4069*0Sstevel@tonic-gate 	struct snode *sp = VTOS(VTOCVP(sdp->vp));
4070*0Sstevel@tonic-gate 
4071*0Sstevel@tonic-gate 	return (e_ddi_copytodev(sp->s_dip,
4072*0Sstevel@tonic-gate 	    (off_t)(uaddr - seg->s_base), kaddr, devaddr, len));
4073*0Sstevel@tonic-gate }
4074