bio.c revision 1.7
1/*	$NetBSD: bio.c,v 1.7 2008/03/03 09:48:28 xtraeme 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.7 2008/03/03 09:48:28 xtraeme 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
49struct bio_mapping {
50	LIST_ENTRY(bio_mapping) bm_link;
51	struct device *bm_dev;
52	int (*bm_ioctl)(struct device *, u_long, void *);
53};
54
55static LIST_HEAD(, bio_mapping) bios = LIST_HEAD_INITIALIZER(bios);
56static kmutex_t bio_lock;
57static bool bio_lock_initialized = false;
58
59static void	bio_initialize(void);
60static int	bioclose(dev_t, int, int, struct lwp *);
61static int	bioioctl(dev_t, u_long, void *, int, struct lwp *);
62static int	bioopen(dev_t, int, int, struct lwp *);
63
64static int	bio_delegate_ioctl(void *, u_long, void *);
65static struct	bio_mapping *bio_lookup(char *);
66static int	bio_validate(void *);
67
68void	bioattach(int);
69
70const struct cdevsw bio_cdevsw = {
71        bioopen, bioclose, noread, nowrite, bioioctl,
72        nostop, notty, nopoll, nommap, nokqfilter, D_OTHER | D_MPSAFE
73};
74
75
76static void
77bio_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
86void
87bioattach(int nunits)
88{
89	if (!bio_lock_initialized)
90		bio_initialize();
91}
92
93static int
94bioopen(dev_t dev, int flags, int mode, struct lwp *l)
95{
96	return 0;
97}
98
99static int
100bioclose(dev_t dev, int flags, int mode, struct lwp *l)
101{
102	return 0;
103}
104
105static int
106bioioctl(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
224int
225bio_register(struct device *dev, int (*ioctl)(struct device *, 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
243void
244bio_unregister(struct device *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
260static struct bio_mapping *
261bio_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, bm->bm_dev->dv_xname) == 0) {
268			mutex_exit(&bio_lock);
269			return bm;
270		}
271	}
272	mutex_exit(&bio_lock);
273	return NULL;
274}
275
276static int
277bio_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
288static int
289bio_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