xref: /dragonfly/sys/kern/kern_conf.c (revision f4e72adab9f09f0dff7be4e0769d3774f24e7f39)
1 /*-
2  * Parts Copyright (c) 1995 Terrence R. Lambert
3  * Copyright (c) 1995 Julian R. Elischer
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *      This product includes software developed by Terrence R. Lambert.
17  * 4. The name Terrence R. Lambert may not be used to endorse or promote
18  *    products derived from this software without specific prior written
19  *    permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY Julian R. Elischer ``AS IS'' AND ANY
22  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * $FreeBSD: src/sys/kern/kern_conf.c,v 1.73.2.3 2003/03/10 02:18:25 imp Exp $
34  */
35 
36 #include <sys/param.h>
37 #include <sys/kernel.h>
38 #include <sys/sysctl.h>
39 #include <sys/systm.h>
40 #include <sys/module.h>
41 #include <sys/conf.h>
42 #include <sys/vnode.h>
43 #include <sys/queue.h>
44 #include <sys/device.h>
45 #include <sys/disk.h>
46 #include <machine/stdarg.h>
47 
48 #include <sys/sysref2.h>
49 
50 #include <sys/devfs.h>
51 
52 static int dev_ref_debug = 0;
53 SYSCTL_INT(_debug, OID_AUTO, dev_refs, CTLFLAG_RW, &dev_ref_debug, 0,
54     "Toggle device reference debug output");
55 
56 /*
57  * cdev_t and u_dev_t primitives.  Note that the major number is always
58  * extracted from si_umajor, not from si_devsw, because si_devsw is replaced
59  * when a device is destroyed.
60  */
61 int
major(cdev_t dev)62 major(cdev_t dev)
63 {
64           if (dev == NULL)
65                     return NOUDEV;
66           return(dev->si_umajor);
67 }
68 
69 int
minor(cdev_t dev)70 minor(cdev_t dev)
71 {
72           if (dev == NULL)
73                     return NOUDEV;
74           return(dev->si_uminor);
75 }
76 
77 /*
78  * Compatibility function with old dev_t format to convert the
79  * non-consecutive minor space into a consecutive minor space.
80  */
81 int
lminor(cdev_t dev)82 lminor(cdev_t dev)
83 {
84           int y;
85 
86           if (dev == NULL)
87                     return NOUDEV;
88           y = dev->si_uminor;
89           if (y & 0x0000ff00)
90                     return NOUDEV;
91           return ((y & 0xff) | (y >> 8));
92 }
93 
94 /*
95  * Convert a device pointer to an old style device number.  Return NOUDEV
96  * if the device is invalid or if the device (maj,min) cannot be converted
97  * to an old style dev_t.
98  */
99 dev_t
devid_from_dev(cdev_t dev)100 devid_from_dev(cdev_t dev)
101 {
102           if (dev == NULL)
103                     return NOUDEV;
104 
105           return (dev_t)dev->si_inode;
106 }
107 
108 /*
109  * Convert a device number to a device pointer.  The device is referenced
110  * ad-hoc, meaning that the caller should call reference_dev() if it wishes
111  * to keep ahold of the returned structure long term.
112  *
113  * The returned device is associated with the currently installed cdevsw
114  * for the requested major number.  NULL is returned if the major number
115  * has not been registered.
116  */
117 cdev_t
dev_from_devid(dev_t x,int b)118 dev_from_devid(dev_t x, int b)
119 {
120           if (x == NOUDEV || b != 0)
121                     return(NULL);
122 
123           return devfs_find_device_by_devid(x);
124 }
125 
126 int
dev_is_good(cdev_t dev)127 dev_is_good(cdev_t dev)
128 {
129           if (dev != NULL && dev->si_ops != &dead_dev_ops)
130                     return(1);
131           return(0);
132 }
133 
134 /*
135  * Various user device number extraction and conversion routines
136  */
137 int
uminor(dev_t dev)138 uminor(dev_t dev)
139 {
140           if (dev == NOUDEV)
141                     return(-1);
142           return(dev & 0xffff00ff);
143 }
144 
145 int
umajor(dev_t dev)146 umajor(dev_t dev)
147 {
148           if (dev == NOUDEV)
149                     return(-1);
150           return((dev & 0xff00) >> 8);
151 }
152 
153 dev_t
makeudev(int x,int y)154 makeudev(int x, int y)
155 {
156           if ((x & 0xffffff00) || (y & 0x0000ff00))
157                     return NOUDEV;
158           return ((x << 8) | y);
159 }
160 
161 /*
162  * Put a NUL-terminated ASCII number (base == 32) for use as device suffix.
163  * The buffer pointed to by `nbuf' must have length >= MAKEDEV_MINNBUF.
164  */
165 char *
makedev_unit_b32(char * nbuf,uintmax_t num)166 makedev_unit_b32(char *nbuf, uintmax_t num)
167 {
168           char *p = &nbuf[MAKEDEV_MINNBUF - 1];
169 
170           *p = '\0';
171           do {
172                     *--p = hex2ascii(num % 32);
173           } while (num /= 32);
174           return (p);
175 }
176 
177 /*
178  * Create an internal or external device.
179  *
180  * This routine creates and returns an unreferenced ad-hoc entry for the
181  * device which will remain intact until the device is destroyed.  If the
182  * caller intends to store the device pointer it must call reference_dev()
183  * to retain a real reference to the device.
184  *
185  * If an entry already exists, this function will set (or override)
186  * its cred requirements and name (XXX DEVFS interface).
187  */
188 cdev_t
make_dev(struct dev_ops * ops,int minor,uid_t uid,gid_t gid,int perms,const char * fmt,...)189 make_dev(struct dev_ops *ops, int minor, uid_t uid, gid_t gid,
190           int perms, const char *fmt, ...)
191 {
192           cdev_t    devfs_dev;
193           __va_list ap;
194 
195           /*
196            * compile the cdevsw and install the device
197            */
198           compile_dev_ops(ops);
199 
200           devfs_dev = devfs_new_cdev(ops, minor, NULL);
201           __va_start(ap, fmt);
202           kvsnprintf(devfs_dev->si_name, sizeof(devfs_dev->si_name), fmt, ap);
203           __va_end(ap);
204 
205           devfs_debug(DEVFS_DEBUG_INFO,
206                         "make_dev called for %s\n",
207                         devfs_dev->si_name);
208           devfs_create_dev(devfs_dev, uid, gid, perms);
209 
210           return (devfs_dev);
211 }
212 
213 /*
214  * make_dev_covering has equivalent functionality to make_dev, except that it
215  * also takes the dev_ops of the underlying device. Hence this function should
216  * only be used by systems and drivers which create devices covering others
217  */
218 cdev_t
make_dev_covering(struct dev_ops * ops,struct dev_ops * bops,int minor,uid_t uid,gid_t gid,int perms,const char * fmt,...)219 make_dev_covering(struct dev_ops *ops, struct dev_ops *bops, int minor,
220               uid_t uid, gid_t gid, int perms, const char *fmt, ...)
221 {
222           cdev_t    devfs_dev;
223           __va_list ap;
224 
225           /*
226            * compile the cdevsw and install the device
227            */
228           compile_dev_ops(ops);
229 
230           devfs_dev = devfs_new_cdev(ops, minor, bops);
231           __va_start(ap, fmt);
232           kvsnprintf(devfs_dev->si_name, sizeof(devfs_dev->si_name), fmt, ap);
233           __va_end(ap);
234 
235           devfs_debug(DEVFS_DEBUG_INFO,
236                         "make_dev called for %s\n",
237                         devfs_dev->si_name);
238           devfs_create_dev(devfs_dev, uid, gid, perms);
239 
240           return (devfs_dev);
241 }
242 
243 
244 
245 cdev_t
make_only_devfs_dev(struct dev_ops * ops,int minor,uid_t uid,gid_t gid,int perms,const char * fmt,...)246 make_only_devfs_dev(struct dev_ops *ops, int minor, uid_t uid, gid_t gid,
247           int perms, const char *fmt, ...)
248 {
249           cdev_t    devfs_dev;
250           __va_list ap;
251 
252           /*
253            * compile the cdevsw and install the device
254            */
255           compile_dev_ops(ops);
256           devfs_dev = devfs_new_cdev(ops, minor, NULL);
257 
258           /*
259            * Set additional fields (XXX DEVFS interface goes here)
260            */
261           __va_start(ap, fmt);
262           kvsnprintf(devfs_dev->si_name, sizeof(devfs_dev->si_name), fmt, ap);
263           __va_end(ap);
264 
265           devfs_create_dev(devfs_dev, uid, gid, perms);
266 
267           return (devfs_dev);
268 }
269 
270 cdev_t
make_only_dev(struct dev_ops * ops,int minor,uid_t uid,gid_t gid,int perms,const char * fmt,...)271 make_only_dev(struct dev_ops *ops, int minor, uid_t uid, gid_t gid,
272           int perms, const char *fmt, ...)
273 {
274           cdev_t    devfs_dev;
275           __va_list ap;
276 
277           /*
278            * compile the cdevsw and install the device
279            */
280           compile_dev_ops(ops);
281           devfs_dev = devfs_new_cdev(ops, minor, NULL);
282           devfs_dev->si_perms = perms;
283           devfs_dev->si_uid = uid;
284           devfs_dev->si_gid = gid;
285 
286           /*
287            * Set additional fields (XXX DEVFS interface goes here)
288            */
289           __va_start(ap, fmt);
290           kvsnprintf(devfs_dev->si_name, sizeof(devfs_dev->si_name), fmt, ap);
291           __va_end(ap);
292 
293           reference_dev(devfs_dev);
294 
295           return (devfs_dev);
296 }
297 
298 cdev_t
make_only_dev_covering(struct dev_ops * ops,struct dev_ops * bops,int minor,uid_t uid,gid_t gid,int perms,const char * fmt,...)299 make_only_dev_covering(struct dev_ops *ops, struct dev_ops *bops, int minor,
300           uid_t uid, gid_t gid, int perms, const char *fmt, ...)
301 {
302           cdev_t    devfs_dev;
303           __va_list ap;
304 
305           /*
306            * compile the cdevsw and install the device
307            */
308           compile_dev_ops(ops);
309           devfs_dev = devfs_new_cdev(ops, minor, bops);
310           devfs_dev->si_perms = perms;
311           devfs_dev->si_uid = uid;
312           devfs_dev->si_gid = gid;
313 
314           /*
315            * Set additional fields (XXX DEVFS interface goes here)
316            */
317           __va_start(ap, fmt);
318           kvsnprintf(devfs_dev->si_name, sizeof(devfs_dev->si_name), fmt, ap);
319           __va_end(ap);
320 
321           reference_dev(devfs_dev);
322 
323           return (devfs_dev);
324 }
325 
326 void
destroy_only_dev(cdev_t dev)327 destroy_only_dev(cdev_t dev)
328 {
329           release_dev(dev);
330           release_dev(dev);
331           release_dev(dev);
332 }
333 
334 /*
335  * destroy_dev() removes the adhoc association for a device and revectors
336  * its ops to &dead_dev_ops.
337  *
338  * This routine releases the reference count associated with the ADHOC
339  * entry, plus releases the reference count held by the caller.  What this
340  * means is that you should not call destroy_dev(make_dev(...)), because
341  * make_dev() does not bump the reference count (beyond what it needs to
342  * create the ad-hoc association).  Any procedure that intends to destroy
343  * a device must have its own reference to it first.
344  */
345 void
destroy_dev(cdev_t dev)346 destroy_dev(cdev_t dev)
347 {
348           if (dev) {
349                     devfs_debug(DEVFS_DEBUG_DEBUG,
350                                   "destroy_dev called for %s\n",
351                                   dev->si_name);
352                     devfs_destroy_dev(dev);
353           }
354 }
355 
356 /*
357  * Make sure all asynchronous disk and devfs related operations have
358  * completed.
359  *
360  * Typically called prior to mountroot to ensure that all disks have
361  * been completely probed and on module unload to ensure that ops
362  * structures have been dereferenced.
363  */
364 void
sync_devs(void)365 sync_devs(void)
366 {
367           disk_config(NULL);
368           devfs_config();
369           disk_config(NULL);
370           devfs_config();
371 }
372 
373 int
make_dev_alias(cdev_t target,const char * fmt,...)374 make_dev_alias(cdev_t target, const char *fmt, ...)
375 {
376           __va_list ap;
377           char *name;
378 
379           __va_start(ap, fmt);
380           kvasnprintf(&name, PATH_MAX, fmt, ap);
381           __va_end(ap);
382 
383           devfs_make_alias(name, target);
384           kvasfree(&name);
385 
386           return 0;
387 }
388 
389 int
destroy_dev_alias(cdev_t target,const char * fmt,...)390 destroy_dev_alias(cdev_t target, const char *fmt, ...)
391 {
392           __va_list ap;
393           char *name;
394 
395           __va_start(ap, fmt);
396           kvasnprintf(&name, PATH_MAX, fmt, ap);
397           __va_end(ap);
398 
399           devfs_destroy_alias(name, target);
400           kvasfree(&name);
401 
402           return 0;
403 }
404 
405 extern struct dev_ops default_dev_ops;
406 
407 cdev_t
make_autoclone_dev(struct dev_ops * ops,struct devfs_bitmap * bitmap,d_clone_t * nhandler,uid_t uid,gid_t gid,int perms,const char * fmt,...)408 make_autoclone_dev(struct dev_ops *ops, struct devfs_bitmap *bitmap,
409                     d_clone_t *nhandler, uid_t uid, gid_t gid, int perms, const char *fmt, ...)
410 {
411           __va_list ap;
412           cdev_t dev;
413           char *name;
414 
415           __va_start(ap, fmt);
416           kvasnprintf(&name, PATH_MAX, fmt, ap);
417           __va_end(ap);
418 
419           if (bitmap != NULL)
420                     devfs_clone_bitmap_init(bitmap);
421 
422           devfs_clone_handler_add(name, nhandler);
423           dev = make_dev_covering(&default_dev_ops, ops, 0xffff00ff,
424                            uid, gid, perms, "%s", name);
425           kvasfree(&name);
426           return dev;
427 }
428 
429 void
destroy_autoclone_dev(cdev_t dev,struct devfs_bitmap * bitmap)430 destroy_autoclone_dev(cdev_t dev, struct devfs_bitmap *bitmap)
431 {
432           if (dev == NULL)
433                     return;
434 
435           devfs_clone_handler_del(dev->si_name);
436 
437           if (bitmap != NULL)
438                     devfs_clone_bitmap_uninit(bitmap);
439 
440           destroy_dev(dev);
441 }
442 
443 
444 /*
445  * Add a reference to a device.  Callers generally add their own references
446  * when they are going to store a device node in a variable for long periods
447  * of time, to prevent a disassociation from free()ing the node.
448  *
449  * Also note that a caller that intends to call destroy_dev() must first
450  * obtain a reference on the device.  The ad-hoc reference you get with
451  * make_dev() and friends is NOT sufficient to be able to call destroy_dev().
452  */
453 cdev_t
reference_dev(cdev_t dev)454 reference_dev(cdev_t dev)
455 {
456           //kprintf("reference_dev\n");
457 
458           if (dev != NULL) {
459                     sysref_get(&dev->si_sysref);
460                     if (dev_ref_debug & 2) {
461                               kprintf("reference dev %p %s(minor=%08x) refs=%d\n",
462                                   dev, devtoname(dev), dev->si_uminor,
463                                   dev->si_sysref.refcnt);
464                     }
465           }
466           return(dev);
467 }
468 
469 /*
470  * release a reference on a device.  The device will be terminated when the
471  * last reference has been released.
472  *
473  * NOTE: we must use si_umajor to figure out the original major number,
474  * because si_ops could already be pointing at dead_dev_ops.
475  */
476 void
release_dev(cdev_t dev)477 release_dev(cdev_t dev)
478 {
479           //kprintf("release_dev\n");
480 
481           if (dev == NULL)
482                     return;
483           sysref_put(&dev->si_sysref);
484 }
485 
486 const char *
devtoname(cdev_t dev)487 devtoname(cdev_t dev)
488 {
489           int mynor;
490           int len;
491           char *p;
492           const char *dname;
493 
494           if (dev == NULL)
495                     return("#nodev");
496           if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') {
497                     p = dev->si_name;
498                     len = sizeof(dev->si_name);
499                     if ((dname = dev_dname(dev)) != NULL)
500                               ksnprintf(p, len, "#%s/", dname);
501                     else
502                               ksnprintf(p, len, "#%d/", major(dev));
503                     len -= strlen(p);
504                     p += strlen(p);
505                     mynor = minor(dev);
506                     if (mynor < 0 || mynor > 255)
507                               ksnprintf(p, len, "%#x", (u_int)mynor);
508                     else
509                               ksnprintf(p, len, "%d", mynor);
510           }
511           return (dev->si_name);
512 }
513 
514