1/* $NetBSD: bio.c,v 1.13 2015/08/20 14:40:17 christos 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.13 2015/08/20 14:40:17 christos 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#include <dev/sysmon/sysmonvar.h>
49
50#include "ioconf.h"
51
52struct bio_mapping {
53 LIST_ENTRY(bio_mapping) bm_link;
54 device_t bm_dev;
55 int (*bm_ioctl)(device_t, u_long, void *);
56};
57
58static LIST_HEAD(, bio_mapping) bios = LIST_HEAD_INITIALIZER(bios);
59static kmutex_t bio_lock;
60static bool bio_lock_initialized = false;
61
62static void bio_initialize(void);
63static int bioclose(dev_t, int, int, struct lwp *);
64static int bioioctl(dev_t, u_long, void *, int, struct lwp *);
65static int bioopen(dev_t, int, int, struct lwp *);
66
67static int bio_delegate_ioctl(void *, u_long, void *);
68static struct bio_mapping *bio_lookup(char *);
69static int bio_validate(void *);
70
71const struct cdevsw bio_cdevsw = {
72 .d_open = bioopen,
73 .d_close = bioclose,
74 .d_read = noread,
75 .d_write = nowrite,
76 .d_ioctl = bioioctl,
77 .d_stop = nostop,
78 .d_tty = notty,
79 .d_poll = nopoll,
80 .d_mmap = nommap,
81 .d_kqfilter = nokqfilter,
82 .d_discard = nodiscard,
83 .d_flag = D_OTHER | D_MPSAFE
84};
85
86
87static void
88bio_initialize(void)
89{
90 if (bio_lock_initialized)
91 return;
92
93 mutex_init(&bio_lock, MUTEX_DEFAULT, IPL_VM);
94 bio_lock_initialized = true;
95}
96
97void
98bioattach(int nunits)
99{
100 if (!bio_lock_initialized)
101 bio_initialize();
102}
103
104static int
105bioopen(dev_t dev, int flags, int mode, struct lwp *l)
106{
107 return 0;
108}
109
110static int
111bioclose(dev_t dev, int flags, int mode, struct lwp *l)
112{
113 return 0;
114}
115
116static int
117bioioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l)
118{
119 struct bio_locate *locate;
120 struct bio_common *common;
121 char name[16];
122 int error;
123
124 switch(cmd) {
125 case BIOCLOCATE:
126 case BIOCINQ:
127 case BIOCDISK:
128 case BIOCDISK_NOVOL:
129 case BIOCVOL:
130#ifdef COMPAT_30
131 case OBIOCDISK:
132 case OBIOCVOL:
133#endif
134 error = kauth_authorize_device_passthru(l->l_cred, dev,
135 KAUTH_REQ_DEVICE_RAWIO_PASSTHRU_READCONF, addr);
136 if (error)
137 return error;
138 break;
139 case BIOCBLINK:
140 case BIOCSETSTATE:
141 case BIOCVOLOPS:
142 error = kauth_authorize_device_passthru(l->l_cred, dev,
143 KAUTH_REQ_DEVICE_RAWIO_PASSTHRU_WRITECONF, addr);
144 if (error)
145 return error;
146 break;
147 case BIOCALARM: {
148 struct bioc_alarm *alarm = (struct bioc_alarm *)addr;
149 switch (alarm->ba_opcode) {
150 case BIOC_SADISABLE:
151 case BIOC_SAENABLE:
152 case BIOC_SASILENCE:
153 case BIOC_SATEST:
154 error = kauth_authorize_device_passthru(l->l_cred, dev,
155 KAUTH_REQ_DEVICE_RAWIO_PASSTHRU_WRITECONF, addr);
156 if (error)
157 return error;
158 break;
159 case BIOC_GASTATUS:
160 error = kauth_authorize_device_passthru(l->l_cred, dev,
161 KAUTH_REQ_DEVICE_RAWIO_PASSTHRU_READCONF, addr);
162 if (error)
163 return error;
164 break;
165 default:
166 return EINVAL;
167 }
168 break;
169 }
170 default:
171 return ENOTTY;
172 }
173
174 switch (cmd) {
175 case BIOCLOCATE:
176 locate = addr;
177 error = copyinstr(locate->bl_name, name, sizeof(name), NULL);
178 if (error != 0)
179 return error;
180 locate->bl_cookie = bio_lookup(name);
181 if (locate->bl_cookie == NULL)
182 return ENOENT;
183 break;
184
185 default:
186 common = addr;
187 mutex_enter(&bio_lock);
188 if (!bio_validate(common->bc_cookie)) {
189 mutex_exit(&bio_lock);
190 return ENOENT;
191 }
192 mutex_exit(&bio_lock);
193#ifdef COMPAT_30
194 switch (cmd) {
195 case OBIOCDISK: {
196 struct bioc_disk *bd =
197 malloc(sizeof(*bd), M_DEVBUF, M_WAITOK|M_ZERO);
198
199 (void)memcpy(bd, addr, sizeof(struct obioc_disk));
200 error = bio_delegate_ioctl(common->bc_cookie,
201 BIOCDISK, bd);
202 if (error) {
203 free(bd, M_DEVBUF);
204 return error;
205 }
206
207 (void)memcpy(addr, bd, sizeof(struct obioc_disk));
208 free(bd, M_DEVBUF);
209 return 0;
210 }
211 case OBIOCVOL: {
212 struct bioc_vol *bv =
213 malloc(sizeof(*bv), M_DEVBUF, M_WAITOK|M_ZERO);
214
215 (void)memcpy(bv, addr, sizeof(struct obioc_vol));
216 error = bio_delegate_ioctl(common->bc_cookie,
217 BIOCVOL, bv);
218 if (error) {
219 free(bv, M_DEVBUF);
220 return error;
221 }
222
223 (void)memcpy(addr, bv, sizeof(struct obioc_vol));
224 free(bv, M_DEVBUF);
225 return 0;
226 }
227 }
228#endif
229 error = bio_delegate_ioctl(common->bc_cookie, cmd, addr);
230 return error;
231 }
232 return 0;
233}
234
235int
236bio_register(device_t dev, int (*ioctl)(device_t, u_long, void *))
237{
238 struct bio_mapping *bm;
239
240 if (!bio_lock_initialized)
241 bio_initialize();
242
243 bm = malloc(sizeof(*bm), M_DEVBUF, M_NOWAIT|M_ZERO);
244 if (bm == NULL)
245 return ENOMEM;
246 bm->bm_dev = dev;
247 bm->bm_ioctl = ioctl;
248 mutex_enter(&bio_lock);
249 LIST_INSERT_HEAD(&bios, bm, bm_link);
250 mutex_exit(&bio_lock);
251 return 0;
252}
253
254void
255bio_unregister(device_t dev)
256{
257 struct bio_mapping *bm, *next;
258
259 mutex_enter(&bio_lock);
260 for (bm = LIST_FIRST(&bios); bm != NULL; bm = next) {
261 next = LIST_NEXT(bm, bm_link);
262
263 if (dev == bm->bm_dev) {
264 LIST_REMOVE(bm, bm_link);
265 free(bm, M_DEVBUF);
266 }
267 }
268 mutex_exit(&bio_lock);
269}
270
271static struct bio_mapping *
272bio_lookup(char *name)
273{
274 struct bio_mapping *bm;
275
276 mutex_enter(&bio_lock);
277 LIST_FOREACH(bm, &bios, bm_link) {
278 if (strcmp(name, device_xname(bm->bm_dev)) == 0) {
279 mutex_exit(&bio_lock);
280 return bm;
281 }
282 }
283 mutex_exit(&bio_lock);
284 return NULL;
285}
286
287static int
288bio_validate(void *cookie)
289{
290 struct bio_mapping *bm;
291
292 LIST_FOREACH(bm, &bios, bm_link)
293 if (bm == cookie)
294 return 1;
295
296 return 0;
297}
298
299static int
300bio_delegate_ioctl(void *cookie, u_long cmd, void *addr)
301{
302 struct bio_mapping *bm = cookie;
303
304 return bm->bm_ioctl(bm->bm_dev, cmd, addr);
305}
306
307void
308bio_disk_to_envsys(envsys_data_t *edata, const struct bioc_disk *bd)
309{
310 switch (bd->bd_status) {
311 case BIOC_SDONLINE:
312 edata->value_cur = ENVSYS_DRIVE_ONLINE;
313 edata->state = ENVSYS_SVALID;
314 break;
315 case BIOC_SDOFFLINE:
316 edata->value_cur = ENVSYS_DRIVE_OFFLINE;
317 edata->state = ENVSYS_SCRITICAL;
318 break;
319 default:
320 edata->value_cur = ENVSYS_DRIVE_FAIL;
321 edata->state = ENVSYS_SCRITICAL;
322 break;
323 }
324}
325
326void
327bio_vol_to_envsys(envsys_data_t *edata, const struct bioc_vol *bv)
328{
329 switch (bv->bv_status) {
330 case BIOC_SVOFFLINE:
331 edata->value_cur = ENVSYS_DRIVE_OFFLINE;
332 edata->state = ENVSYS_SCRITICAL;
333 break;
334 case BIOC_SVDEGRADED:
335 edata->value_cur = ENVSYS_DRIVE_PFAIL;
336 edata->state = ENVSYS_SCRITICAL;
337 break;
338 case BIOC_SVBUILDING:
339 edata->value_cur = ENVSYS_DRIVE_BUILD;
340 edata->state = ENVSYS_SVALID;
341 break;
342 case BIOC_SVMIGRATING:
343 edata->value_cur = ENVSYS_DRIVE_MIGRATING;
344 edata->state = ENVSYS_SVALID;
345 break;
346 case BIOC_SVCHECKING:
347 edata->value_cur = ENVSYS_DRIVE_CHECK;
348 edata->state = ENVSYS_SVALID;
349 break;
350 case BIOC_SVREBUILD:
351 edata->value_cur = ENVSYS_DRIVE_REBUILD;
352 edata->state = ENVSYS_SCRITICAL;
353 break;
354 case BIOC_SVSCRUB:
355 case BIOC_SVONLINE:
356 edata->value_cur = ENVSYS_DRIVE_ONLINE;
357 edata->state = ENVSYS_SVALID;
358 break;
359 case BIOC_SVINVALID:
360 /* FALLTHROUGH */
361 default:
362 edata->value_cur = ENVSYS_DRIVE_EMPTY; /* unknown state */
363 edata->state = ENVSYS_SINVALID;
364 break;
365 }
366}
367