xref: /dflybsd-src/sys/kern/kern_conf.c (revision 49ecae83bf6e5a8f787f7d801ddf305dce90cde5)
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 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
62 major(cdev_t dev)
63 {
64 	if (dev == NULL)
65 		return NOUDEV;
66 	return(dev->si_umajor);
67 }
68 
69 int
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 udev_t format to convert the
79  * non-consecutive minor space into a consecutive minor space.
80  */
81 int
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 udev_t.
98  */
99 udev_t
100 dev2udev(cdev_t dev)
101 {
102 	if (dev == NULL)
103 		return NOUDEV;
104 
105 	return (udev_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
118 udev2dev(udev_t x, int b)
119 {
120 	if (x == NOUDEV || b != 0)
121 		return(NULL);
122 
123 	return devfs_find_device_by_udev(x);
124 }
125 
126 int
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
138 uminor(udev_t dev)
139 {
140 	if (dev == NOUDEV)
141 		return(-1);
142 	return(dev & 0xffff00ff);
143 }
144 
145 int
146 umajor(udev_t dev)
147 {
148 	if (dev == NOUDEV)
149 		return(-1);
150 	return((dev & 0xff00) >> 8);
151 }
152 
153 udev_t
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 *
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
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 	kvsnrprintf(devfs_dev->si_name, sizeof(devfs_dev->si_name),
203 		    32, fmt, ap);
204 	__va_end(ap);
205 
206 	devfs_debug(DEVFS_DEBUG_INFO,
207 		    "make_dev called for %s\n",
208 		    devfs_dev->si_name);
209 	devfs_create_dev(devfs_dev, uid, gid, perms);
210 
211 	return (devfs_dev);
212 }
213 
214 /*
215  * make_dev_covering has equivalent functionality to make_dev, except that it
216  * also takes the dev_ops of the underlying device. Hence this function should
217  * only be used by systems and drivers which create devices covering others
218  */
219 cdev_t
220 make_dev_covering(struct dev_ops *ops, struct dev_ops *bops, int minor,
221 	    uid_t uid, gid_t gid, int perms, const char *fmt, ...)
222 {
223 	cdev_t	devfs_dev;
224 	__va_list ap;
225 
226 	/*
227 	 * compile the cdevsw and install the device
228 	 */
229 	compile_dev_ops(ops);
230 
231 	devfs_dev = devfs_new_cdev(ops, minor, bops);
232 	__va_start(ap, fmt);
233 	kvsnrprintf(devfs_dev->si_name, sizeof(devfs_dev->si_name),
234 		    32, fmt, ap);
235 	__va_end(ap);
236 
237 	devfs_debug(DEVFS_DEBUG_INFO,
238 		    "make_dev called for %s\n",
239 		    devfs_dev->si_name);
240 	devfs_create_dev(devfs_dev, uid, gid, perms);
241 
242 	return (devfs_dev);
243 }
244 
245 
246 
247 cdev_t
248 make_only_devfs_dev(struct dev_ops *ops, int minor, uid_t uid, gid_t gid,
249 	int perms, const char *fmt, ...)
250 {
251 	cdev_t	devfs_dev;
252 	__va_list ap;
253 
254 	/*
255 	 * compile the cdevsw and install the device
256 	 */
257 	compile_dev_ops(ops);
258 	devfs_dev = devfs_new_cdev(ops, minor, NULL);
259 
260 	/*
261 	 * Set additional fields (XXX DEVFS interface goes here)
262 	 */
263 	__va_start(ap, fmt);
264 	kvsnrprintf(devfs_dev->si_name, sizeof(devfs_dev->si_name),
265 		    32, fmt, ap);
266 	__va_end(ap);
267 
268 	devfs_create_dev(devfs_dev, uid, gid, perms);
269 
270 	return (devfs_dev);
271 }
272 
273 cdev_t
274 make_only_dev(struct dev_ops *ops, int minor, uid_t uid, gid_t gid,
275 	int perms, const char *fmt, ...)
276 {
277 	cdev_t	devfs_dev;
278 	__va_list ap;
279 
280 	/*
281 	 * compile the cdevsw and install the device
282 	 */
283 	compile_dev_ops(ops);
284 	devfs_dev = devfs_new_cdev(ops, minor, NULL);
285 	devfs_dev->si_perms = perms;
286 	devfs_dev->si_uid = uid;
287 	devfs_dev->si_gid = gid;
288 
289 	/*
290 	 * Set additional fields (XXX DEVFS interface goes here)
291 	 */
292 	__va_start(ap, fmt);
293 	kvsnrprintf(devfs_dev->si_name, sizeof(devfs_dev->si_name),
294 		    32, fmt, ap);
295 	__va_end(ap);
296 
297 	reference_dev(devfs_dev);
298 
299 	return (devfs_dev);
300 }
301 
302 cdev_t
303 make_only_dev_covering(struct dev_ops *ops, struct dev_ops *bops, int minor,
304 	uid_t uid, gid_t gid, int perms, const char *fmt, ...)
305 {
306 	cdev_t	devfs_dev;
307 	__va_list ap;
308 
309 	/*
310 	 * compile the cdevsw and install the device
311 	 */
312 	compile_dev_ops(ops);
313 	devfs_dev = devfs_new_cdev(ops, minor, bops);
314 	devfs_dev->si_perms = perms;
315 	devfs_dev->si_uid = uid;
316 	devfs_dev->si_gid = gid;
317 
318 	/*
319 	 * Set additional fields (XXX DEVFS interface goes here)
320 	 */
321 	__va_start(ap, fmt);
322 	kvsnrprintf(devfs_dev->si_name, sizeof(devfs_dev->si_name),
323 		    32, fmt, ap);
324 	__va_end(ap);
325 
326 	reference_dev(devfs_dev);
327 
328 	return (devfs_dev);
329 }
330 
331 void
332 destroy_only_dev(cdev_t dev)
333 {
334 	release_dev(dev);
335 	release_dev(dev);
336 	release_dev(dev);
337 }
338 
339 /*
340  * destroy_dev() removes the adhoc association for a device and revectors
341  * its ops to &dead_dev_ops.
342  *
343  * This routine releases the reference count associated with the ADHOC
344  * entry, plus releases the reference count held by the caller.  What this
345  * means is that you should not call destroy_dev(make_dev(...)), because
346  * make_dev() does not bump the reference count (beyond what it needs to
347  * create the ad-hoc association).  Any procedure that intends to destroy
348  * a device must have its own reference to it first.
349  */
350 void
351 destroy_dev(cdev_t dev)
352 {
353 	if (dev) {
354 		devfs_debug(DEVFS_DEBUG_DEBUG,
355 			    "destroy_dev called for %s\n",
356 			    dev->si_name);
357 		devfs_destroy_dev(dev);
358 	}
359 }
360 
361 /*
362  * Make sure all asynchronous disk and devfs related operations have
363  * completed.
364  *
365  * Typically called prior to mountroot to ensure that all disks have
366  * been completely probed and on module unload to ensure that ops
367  * structures have been dereferenced.
368  */
369 void
370 sync_devs(void)
371 {
372 	disk_config(NULL);
373 	devfs_config();
374 	disk_config(NULL);
375 	devfs_config();
376 }
377 
378 int
379 make_dev_alias(cdev_t target, const char *fmt, ...)
380 {
381 	__va_list ap;
382 	char *name;
383 
384 	__va_start(ap, fmt);
385 	kvasnrprintf(&name, PATH_MAX, 32, fmt, ap);
386 	__va_end(ap);
387 
388 	devfs_make_alias(name, target);
389 	kvasfree(&name);
390 
391 	return 0;
392 }
393 
394 int
395 destroy_dev_alias(cdev_t target, const char *fmt, ...)
396 {
397 	__va_list ap;
398 	char *name;
399 
400 	__va_start(ap, fmt);
401 	kvasnrprintf(&name, PATH_MAX, 32, fmt, ap);
402 	__va_end(ap);
403 
404 	devfs_destroy_alias(name, target);
405 	kvasfree(&name);
406 
407 	return 0;
408 }
409 
410 extern struct dev_ops default_dev_ops;
411 
412 cdev_t
413 make_autoclone_dev(struct dev_ops *ops, struct devfs_bitmap *bitmap,
414 		d_clone_t *nhandler, uid_t uid, gid_t gid, int perms, const char *fmt, ...)
415 {
416 	__va_list ap;
417 	cdev_t dev;
418 	char *name;
419 
420 	__va_start(ap, fmt);
421 	kvasnrprintf(&name, PATH_MAX, 32, fmt, ap);
422 	__va_end(ap);
423 
424 	if (bitmap != NULL)
425 		devfs_clone_bitmap_init(bitmap);
426 
427 	devfs_clone_handler_add(name, nhandler);
428 	dev = make_dev_covering(&default_dev_ops, ops, 0xffff00ff,
429 		       uid, gid, perms, "%s", name);
430 	kvasfree(&name);
431 	return dev;
432 }
433 
434 void
435 destroy_autoclone_dev(cdev_t dev, struct devfs_bitmap *bitmap)
436 {
437 	if (dev == NULL)
438 		return;
439 
440 	devfs_clone_handler_del(dev->si_name);
441 
442 	if (bitmap != NULL)
443 		devfs_clone_bitmap_uninit(bitmap);
444 
445 	destroy_dev(dev);
446 }
447 
448 
449 /*
450  * Add a reference to a device.  Callers generally add their own references
451  * when they are going to store a device node in a variable for long periods
452  * of time, to prevent a disassociation from free()ing the node.
453  *
454  * Also note that a caller that intends to call destroy_dev() must first
455  * obtain a reference on the device.  The ad-hoc reference you get with
456  * make_dev() and friends is NOT sufficient to be able to call destroy_dev().
457  */
458 cdev_t
459 reference_dev(cdev_t dev)
460 {
461 	//kprintf("reference_dev\n");
462 
463 	if (dev != NULL) {
464 		sysref_get(&dev->si_sysref);
465 		if (dev_ref_debug & 2) {
466 			kprintf("reference dev %p %s(minor=%08x) refs=%d\n",
467 			    dev, devtoname(dev), dev->si_uminor,
468 			    dev->si_sysref.refcnt);
469 		}
470 	}
471 	return(dev);
472 }
473 
474 /*
475  * release a reference on a device.  The device will be terminated when the
476  * last reference has been released.
477  *
478  * NOTE: we must use si_umajor to figure out the original major number,
479  * because si_ops could already be pointing at dead_dev_ops.
480  */
481 void
482 release_dev(cdev_t dev)
483 {
484 	//kprintf("release_dev\n");
485 
486 	if (dev == NULL)
487 		return;
488 	sysref_put(&dev->si_sysref);
489 }
490 
491 const char *
492 devtoname(cdev_t dev)
493 {
494 	int mynor;
495 	int len;
496 	char *p;
497 	const char *dname;
498 
499 	if (dev == NULL)
500 		return("#nodev");
501 	if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') {
502 		p = dev->si_name;
503 		len = sizeof(dev->si_name);
504 		if ((dname = dev_dname(dev)) != NULL)
505 			ksnprintf(p, len, "#%s/", dname);
506 		else
507 			ksnprintf(p, len, "#%d/", major(dev));
508 		len -= strlen(p);
509 		p += strlen(p);
510 		mynor = minor(dev);
511 		if (mynor < 0 || mynor > 255)
512 			ksnprintf(p, len, "%#x", (u_int)mynor);
513 		else
514 			ksnprintf(p, len, "%d", mynor);
515 	}
516 	return (dev->si_name);
517 }
518 
519