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 | |
52 | struct 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 | |
58 | static LIST_HEAD(, bio_mapping) bios = LIST_HEAD_INITIALIZER(bios); |
59 | static kmutex_t bio_lock; |
60 | static bool bio_lock_initialized = false; |
61 | |
62 | static void bio_initialize(void); |
63 | static int bioclose(dev_t, int, int, struct lwp *); |
64 | static int bioioctl(dev_t, u_long, void *, int, struct lwp *); |
65 | static int bioopen(dev_t, int, int, struct lwp *); |
66 | |
67 | static int bio_delegate_ioctl(void *, u_long, void *); |
68 | static struct bio_mapping *bio_lookup(char *); |
69 | static int bio_validate(void *); |
70 | |
71 | const 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 | |
87 | static void |
88 | bio_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 | |
97 | void |
98 | bioattach(int nunits) |
99 | { |
100 | if (!bio_lock_initialized) |
101 | bio_initialize(); |
102 | } |
103 | |
104 | static int |
105 | bioopen(dev_t dev, int flags, int mode, struct lwp *l) |
106 | { |
107 | return 0; |
108 | } |
109 | |
110 | static int |
111 | bioclose(dev_t dev, int flags, int mode, struct lwp *l) |
112 | { |
113 | return 0; |
114 | } |
115 | |
116 | static int |
117 | bioioctl(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 | |
235 | int |
236 | bio_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 | |
254 | void |
255 | bio_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 | |
271 | static struct bio_mapping * |
272 | bio_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 | |
287 | static int |
288 | bio_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 | |
299 | static int |
300 | bio_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 | |
307 | void |
308 | bio_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 | |
326 | void |
327 | bio_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 | |