xref: /netbsd/sys/dev/bio.c (revision 6550d01e)
1 /*	$NetBSD: bio.c,v 1.9 2009/05/07 12:15:33 cegger Exp $ */
2 /*	$OpenBSD: bio.c,v 1.9 2007/03/20 02:35:55 marco Exp $	*/
3 
4 /*
5  * Copyright (c) 2002 Niklas Hallqvist.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 /* A device controller ioctl tunnelling device.  */
29 
30 #include <sys/cdefs.h>
31 __KERNEL_RCSID(0, "$NetBSD: bio.c,v 1.9 2009/05/07 12:15:33 cegger Exp $");
32 
33 #include "opt_compat_netbsd.h"
34 
35 #include <sys/param.h>
36 #include <sys/conf.h>
37 #include <sys/device.h>
38 #include <sys/event.h>
39 #include <sys/ioctl.h>
40 #include <sys/malloc.h>
41 #include <sys/queue.h>
42 #include <sys/systm.h>
43 #include <sys/mutex.h>
44 #include <sys/proc.h>
45 #include <sys/kauth.h>
46 
47 #include <dev/biovar.h>
48 
49 struct bio_mapping {
50 	LIST_ENTRY(bio_mapping) bm_link;
51 	device_t bm_dev;
52 	int (*bm_ioctl)(device_t, u_long, void *);
53 };
54 
55 static LIST_HEAD(, bio_mapping) bios = LIST_HEAD_INITIALIZER(bios);
56 static kmutex_t bio_lock;
57 static bool bio_lock_initialized = false;
58 
59 static void	bio_initialize(void);
60 static int	bioclose(dev_t, int, int, struct lwp *);
61 static int	bioioctl(dev_t, u_long, void *, int, struct lwp *);
62 static int	bioopen(dev_t, int, int, struct lwp *);
63 
64 static int	bio_delegate_ioctl(void *, u_long, void *);
65 static struct	bio_mapping *bio_lookup(char *);
66 static int	bio_validate(void *);
67 
68 void	bioattach(int);
69 
70 const struct cdevsw bio_cdevsw = {
71         bioopen, bioclose, noread, nowrite, bioioctl,
72         nostop, notty, nopoll, nommap, nokqfilter, D_OTHER | D_MPSAFE
73 };
74 
75 
76 static void
77 bio_initialize(void)
78 {
79 	if (bio_lock_initialized)
80 		return;
81 
82 	mutex_init(&bio_lock, MUTEX_DEFAULT, IPL_VM);
83 	bio_lock_initialized = true;
84 }
85 
86 void
87 bioattach(int nunits)
88 {
89 	if (!bio_lock_initialized)
90 		bio_initialize();
91 }
92 
93 static int
94 bioopen(dev_t dev, int flags, int mode, struct lwp *l)
95 {
96 	return 0;
97 }
98 
99 static int
100 bioclose(dev_t dev, int flags, int mode, struct lwp *l)
101 {
102 	return 0;
103 }
104 
105 static int
106 bioioctl(dev_t dev, u_long cmd, void *addr, int flag, struct  lwp *l)
107 {
108 	struct bio_locate *locate;
109 	struct bio_common *common;
110 	char name[16];
111 	int error;
112 
113 	switch(cmd) {
114 	case BIOCLOCATE:
115 	case BIOCINQ:
116 	case BIOCDISK:
117 	case BIOCDISK_NOVOL:
118 	case BIOCVOL:
119 #ifdef COMPAT_30
120 	case OBIOCDISK:
121 	case OBIOCVOL:
122 #endif
123 		error = kauth_authorize_device_passthru(l->l_cred, dev,
124 		    KAUTH_REQ_DEVICE_RAWIO_PASSTHRU_READCONF, addr);
125 		if (error)
126 			return error;
127 		break;
128 	case BIOCBLINK:
129 	case BIOCSETSTATE:
130 	case BIOCVOLOPS:
131 		error = kauth_authorize_device_passthru(l->l_cred, dev,
132 		    KAUTH_REQ_DEVICE_RAWIO_PASSTHRU_WRITECONF, addr);
133 		if (error)
134 			return error;
135 		break;
136 	case BIOCALARM: {
137 		struct bioc_alarm *alarm = (struct bioc_alarm *)addr;
138 		switch (alarm->ba_opcode) {
139 		case BIOC_SADISABLE:
140 		case BIOC_SAENABLE:
141 		case BIOC_SASILENCE:
142 		case BIOC_SATEST:
143 			error = kauth_authorize_device_passthru(l->l_cred, dev,
144 			    KAUTH_REQ_DEVICE_RAWIO_PASSTHRU_WRITECONF, addr);
145 			if (error)
146 				return error;
147 			break;
148 		case BIOC_GASTATUS:
149 			error = kauth_authorize_device_passthru(l->l_cred, dev,
150 			    KAUTH_REQ_DEVICE_RAWIO_PASSTHRU_READCONF, addr);
151 			if (error)
152 				return error;
153 			break;
154 		default:
155 			return EINVAL;
156 		}
157 		break;
158 	}
159 	default:
160 		return ENOTTY;
161 	}
162 
163 	switch (cmd) {
164 	case BIOCLOCATE:
165 		locate = addr;
166 		error = copyinstr(locate->bl_name, name, sizeof(name), NULL);
167 		if (error != 0)
168 			return error;
169 		locate->bl_cookie = bio_lookup(name);
170 		if (locate->bl_cookie == NULL)
171 			return ENOENT;
172 		break;
173 
174 	default:
175 		common = addr;
176 		mutex_enter(&bio_lock);
177 		if (!bio_validate(common->bc_cookie)) {
178 			mutex_exit(&bio_lock);
179 			return ENOENT;
180 		}
181 		mutex_exit(&bio_lock);
182 #ifdef COMPAT_30
183 		switch (cmd) {
184 		case OBIOCDISK: {
185 			struct bioc_disk *bd =
186 			    malloc(sizeof(*bd), M_DEVBUF, M_WAITOK|M_ZERO);
187 
188 			(void)memcpy(bd, addr, sizeof(struct obioc_disk));
189 			error = bio_delegate_ioctl(common->bc_cookie,
190 			    BIOCDISK, bd);
191 			if (error) {
192 				free(bd, M_DEVBUF);
193 				return error;
194 			}
195 
196 			(void)memcpy(addr, bd, sizeof(struct obioc_disk));
197 			free(bd, M_DEVBUF);
198 			return 0;
199 		}
200 		case OBIOCVOL: {
201 			struct bioc_vol *bv =
202 			    malloc(sizeof(*bv), M_DEVBUF, M_WAITOK|M_ZERO);
203 
204 			(void)memcpy(bv, addr, sizeof(struct obioc_vol));
205 			error = bio_delegate_ioctl(common->bc_cookie,
206 			    BIOCVOL, bv);
207 			if (error) {
208 				free(bv, M_DEVBUF);
209 				return error;
210 			}
211 
212 			(void)memcpy(addr, bv, sizeof(struct obioc_vol));
213 			free(bv, M_DEVBUF);
214 			return 0;
215 		}
216 		}
217 #endif
218 		error = bio_delegate_ioctl(common->bc_cookie, cmd, addr);
219 		return error;
220 	}
221 	return 0;
222 }
223 
224 int
225 bio_register(device_t dev, int (*ioctl)(device_t, u_long, void *))
226 {
227 	struct bio_mapping *bm;
228 
229 	if (!bio_lock_initialized)
230 		bio_initialize();
231 
232 	bm = malloc(sizeof(*bm), M_DEVBUF, M_NOWAIT|M_ZERO);
233 	if (bm == NULL)
234 		return ENOMEM;
235 	bm->bm_dev = dev;
236 	bm->bm_ioctl = ioctl;
237 	mutex_enter(&bio_lock);
238 	LIST_INSERT_HEAD(&bios, bm, bm_link);
239 	mutex_exit(&bio_lock);
240 	return 0;
241 }
242 
243 void
244 bio_unregister(device_t dev)
245 {
246 	struct bio_mapping *bm, *next;
247 
248 	mutex_enter(&bio_lock);
249 	for (bm = LIST_FIRST(&bios); bm != NULL; bm = next) {
250 		next = LIST_NEXT(bm, bm_link);
251 
252 		if (dev == bm->bm_dev) {
253 			LIST_REMOVE(bm, bm_link);
254 			free(bm, M_DEVBUF);
255 		}
256 	}
257 	mutex_exit(&bio_lock);
258 }
259 
260 static struct bio_mapping *
261 bio_lookup(char *name)
262 {
263 	struct bio_mapping *bm;
264 
265 	mutex_enter(&bio_lock);
266 	LIST_FOREACH(bm, &bios, bm_link) {
267 		if (strcmp(name, device_xname(bm->bm_dev)) == 0) {
268 			mutex_exit(&bio_lock);
269 			return bm;
270 		}
271 	}
272 	mutex_exit(&bio_lock);
273 	return NULL;
274 }
275 
276 static int
277 bio_validate(void *cookie)
278 {
279 	struct bio_mapping *bm;
280 
281 	LIST_FOREACH(bm, &bios, bm_link)
282 		if (bm == cookie)
283 			return 1;
284 
285 	return 0;
286 }
287 
288 static int
289 bio_delegate_ioctl(void *cookie, u_long cmd, void *addr)
290 {
291 	struct bio_mapping *bm = cookie;
292 
293 	return bm->bm_ioctl(bm->bm_dev, cmd, addr);
294 }
295