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