xref: /freebsd/sys/kern/kern_conf.c (revision 4b2eaea43fec8e8792be611dea204071a10b655a)
1 /*-
2  * Copyright (c) 1999-2002 Poul-Henning Kamp
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #include <sys/param.h>
30 #include <sys/kernel.h>
31 #include <sys/systm.h>
32 #include <sys/lock.h>
33 #include <sys/mutex.h>
34 #include <sys/sysctl.h>
35 #include <sys/module.h>
36 #include <sys/malloc.h>
37 #include <sys/conf.h>
38 #include <sys/vnode.h>
39 #include <sys/queue.h>
40 #include <sys/ctype.h>
41 #include <machine/stdarg.h>
42 
43 #ifdef NODEVFS
44 static struct cdevsw 	*cdevsw[NUMCDEVSW];
45 
46 #endif
47 static MALLOC_DEFINE(M_DEVT, "dev_t", "dev_t storage");
48 
49 /*
50  * This is the number of hash-buckets.  Experiements with 'real-life'
51  * udev_t's show that a prime halfway between two powers of two works
52  * best.
53  */
54 #define DEVT_HASH 83
55 
56 /* The number of dev_t's we can create before malloc(9) kick in.  */
57 #define DEVT_STASH 50
58 
59 static struct cdev devt_stash[DEVT_STASH];
60 
61 static LIST_HEAD(, cdev) dev_hash[DEVT_HASH];
62 
63 static LIST_HEAD(, cdev) dev_free;
64 
65 devfs_create_t *devfs_create_hook;
66 devfs_destroy_t *devfs_destroy_hook;
67 
68 static int ready_for_devs;
69 
70 static int free_devt;
71 SYSCTL_INT(_debug, OID_AUTO, free_devt, CTLFLAG_RW, &free_devt, 0, "");
72 
73 #ifdef NO_GEOM
74 /* XXX: This is a hack */
75 void disk_dev_synth(dev_t dev);
76 #endif
77 
78 struct cdevsw *
79 devsw(dev_t dev)
80 {
81 	if (dev->si_devsw)
82 		return (dev->si_devsw);
83 #ifdef NO_GEOM
84 	/* XXX: Hack around our backwards disk code */
85 	disk_dev_synth(dev);
86 #endif
87 	if (dev->si_devsw)
88 		return (dev->si_devsw);
89 #ifndef NODEVFS
90 	return (NULL);
91 #else
92         return(cdevsw[major(dev)]);
93 #endif
94 }
95 
96 /*
97  *  Add a cdevsw entry
98  */
99 
100 int
101 cdevsw_add(struct cdevsw *newentry)
102 {
103 #ifdef NODEVFS
104 	if (newentry->d_maj < 0 || newentry->d_maj >= NUMCDEVSW) {
105 		printf("%s: ERROR: driver has bogus cdevsw->d_maj = %d\n",
106 		    newentry->d_name, newentry->d_maj);
107 		return (EINVAL);
108 	}
109 
110 	if (cdevsw[newentry->d_maj]) {
111 		printf("WARNING: \"%s\" is usurping \"%s\"'s cdevsw[]\n",
112 		    newentry->d_name, cdevsw[newentry->d_maj]->d_name);
113 	}
114 
115 	cdevsw[newentry->d_maj] = newentry;
116 #endif
117 
118 	return (0);
119 }
120 
121 /*
122  *  Remove a cdevsw entry
123  */
124 
125 int
126 cdevsw_remove(struct cdevsw *oldentry)
127 {
128 #ifdef NODEVFS
129 	if (oldentry->d_maj < 0 || oldentry->d_maj >= NUMCDEVSW) {
130 		printf("%s: ERROR: driver has bogus cdevsw->d_maj = %d\n",
131 		    oldentry->d_name, oldentry->d_maj);
132 		return EINVAL;
133 	}
134 
135 	cdevsw[oldentry->d_maj] = NULL;
136 #endif
137 
138 	return 0;
139 }
140 
141 /*
142  * dev_t and u_dev_t primitives
143  */
144 
145 int
146 major(dev_t x)
147 {
148 	if (x == NODEV)
149 		return NOUDEV;
150 	return((x->si_udev >> 8) & 0xff);
151 }
152 
153 int
154 minor(dev_t x)
155 {
156 	if (x == NODEV)
157 		return NOUDEV;
158 	return(x->si_udev & 0xffff00ff);
159 }
160 
161 int
162 dev2unit(dev_t x)
163 {
164 	int i;
165 
166 	if (x == NODEV)
167 		return NOUDEV;
168 	i = minor(x);
169 	return ((i & 0xff) | (i >> 8));
170 }
171 
172 int
173 unit2minor(int unit)
174 {
175 
176 	KASSERT(unit <= 0xffffff, ("Invalid unit (%d) in unit2minor", unit));
177 	return ((unit & 0xff) | ((unit << 8) & ~0xffff));
178 }
179 
180 static dev_t
181 allocdev(void)
182 {
183 	static int stashed;
184 	struct cdev *si;
185 
186 	if (LIST_FIRST(&dev_free)) {
187 		si = LIST_FIRST(&dev_free);
188 		LIST_REMOVE(si, si_hash);
189 	} else if (stashed >= DEVT_STASH) {
190 		MALLOC(si, struct cdev *, sizeof(*si), M_DEVT,
191 		    M_USE_RESERVE | M_ZERO);
192 	} else {
193 		si = devt_stash + stashed++;
194 		bzero(si, sizeof *si);
195 		si->si_flags |= SI_STASHED;
196 	}
197 	LIST_INIT(&si->si_children);
198 	TAILQ_INIT(&si->si_snapshots);
199 	return (si);
200 }
201 
202 dev_t
203 makedev(int x, int y)
204 {
205 	struct cdev *si;
206 	udev_t	udev;
207 	int hash;
208 
209 	if (x == umajor(NOUDEV) && y == uminor(NOUDEV))
210 		panic("makedev of NOUDEV");
211 	udev = (x << 8) | y;
212 	hash = udev % DEVT_HASH;
213 	LIST_FOREACH(si, &dev_hash[hash], si_hash) {
214 		if (si->si_udev == udev)
215 			return (si);
216 	}
217 	si = allocdev();
218 	si->si_udev = udev;
219 	LIST_INSERT_HEAD(&dev_hash[hash], si, si_hash);
220         return (si);
221 }
222 
223 void
224 freedev(dev_t dev)
225 {
226 
227 	if (!free_devt)
228 		return;
229 	if (SLIST_FIRST(&dev->si_hlist))
230 		return;
231 	if (dev->si_devsw || dev->si_drv1 || dev->si_drv2)
232 		return;
233 	LIST_REMOVE(dev, si_hash);
234 	if (dev->si_flags & SI_STASHED) {
235 		bzero(dev, sizeof(*dev));
236 		dev->si_flags |= SI_STASHED;
237 		LIST_INSERT_HEAD(&dev_free, dev, si_hash);
238 	} else {
239 		FREE(dev, M_DEVT);
240 	}
241 }
242 
243 udev_t
244 dev2udev(dev_t x)
245 {
246 	if (x == NODEV)
247 		return NOUDEV;
248 	return (x->si_udev);
249 }
250 
251 dev_t
252 udev2dev(udev_t x, int b)
253 {
254 
255 	if (x == NOUDEV)
256 		return (NODEV);
257 	switch (b) {
258 		case 0:
259 			return makedev(umajor(x), uminor(x));
260 		case 1:
261 			return (NODEV);
262 		default:
263 			Debugger("udev2dev(...,X)");
264 			return NODEV;
265 	}
266 }
267 
268 int
269 uminor(udev_t dev)
270 {
271 	return(dev & 0xffff00ff);
272 }
273 
274 int
275 umajor(udev_t dev)
276 {
277 	return((dev & 0xff00) >> 8);
278 }
279 
280 udev_t
281 makeudev(int x, int y)
282 {
283         return ((x << 8) | y);
284 }
285 
286 dev_t
287 make_dev(struct cdevsw *devsw, int minor, uid_t uid, gid_t gid, int perms, const char *fmt, ...)
288 {
289 	dev_t	dev;
290 	va_list ap;
291 	int i;
292 
293 	KASSERT(umajor(makeudev(devsw->d_maj, minor)) == devsw->d_maj,
294 	    ("Invalid minor (%d) in make_dev", minor));
295 
296 	if (!ready_for_devs) {
297 		printf("WARNING: Driver mistake: make_dev(%s) called before SI_SUB_DRIVERS\n",
298 		       fmt);
299 		/* XXX panic here once drivers are cleaned up */
300 	}
301 
302 	dev = makedev(devsw->d_maj, minor);
303 	if (dev->si_flags & SI_NAMED) {
304 		printf( "WARNING: Driver mistake: repeat make_dev(\"%s\")\n",
305 		    dev->si_name);
306 		panic("don't do that");
307 		return (dev);
308 	}
309 	va_start(ap, fmt);
310 	i = kvprintf(fmt, NULL, dev->si_name, 32, ap);
311 	dev->si_name[i] = '\0';
312 	va_end(ap);
313 	dev->si_devsw = devsw;
314 	dev->si_uid = uid;
315 	dev->si_gid = gid;
316 	dev->si_mode = perms;
317 	dev->si_flags |= SI_NAMED;
318 
319 	if (devfs_create_hook)
320 		devfs_create_hook(dev);
321 	return (dev);
322 }
323 
324 int
325 dev_named(dev_t pdev, const char *name)
326 {
327 	dev_t cdev;
328 
329 	if (strcmp(devtoname(pdev), name) == 0)
330 		return (1);
331 	LIST_FOREACH(cdev, &pdev->si_children, si_siblings)
332 		if (strcmp(devtoname(cdev), name) == 0)
333 			return (1);
334 	return (0);
335 }
336 
337 void
338 dev_depends(dev_t pdev, dev_t cdev)
339 {
340 
341 	cdev->si_parent = pdev;
342 	cdev->si_flags |= SI_CHILD;
343 	LIST_INSERT_HEAD(&pdev->si_children, cdev, si_siblings);
344 }
345 
346 dev_t
347 make_dev_alias(dev_t pdev, const char *fmt, ...)
348 {
349 	dev_t	dev;
350 	va_list ap;
351 	int i;
352 
353 	dev = allocdev();
354 	dev->si_flags |= SI_ALIAS;
355 	dev->si_flags |= SI_NAMED;
356 	dev_depends(pdev, dev);
357 	va_start(ap, fmt);
358 	i = kvprintf(fmt, NULL, dev->si_name, 32, ap);
359 	dev->si_name[i] = '\0';
360 	va_end(ap);
361 
362 	if (devfs_create_hook)
363 		devfs_create_hook(dev);
364 	return (dev);
365 }
366 
367 void
368 revoke_and_destroy_dev(dev_t dev)
369 {
370 	struct vnode *vp;
371 
372 	GIANT_REQUIRED;
373 
374 	vp = SLIST_FIRST(&dev->si_hlist);
375 	if (vp != NULL)
376 		VOP_REVOKE(vp, REVOKEALL);
377 	destroy_dev(dev);
378 }
379 
380 void
381 destroy_dev(dev_t dev)
382 {
383 
384 	if (!(dev->si_flags & SI_NAMED)) {
385 		printf( "WARNING: Driver mistake: destroy_dev on %d/%d\n",
386 		    major(dev), minor(dev));
387 		panic("don't do that");
388 		return;
389 	}
390 
391 	if (devfs_destroy_hook)
392 		devfs_destroy_hook(dev);
393 	if (dev->si_flags & SI_CHILD) {
394 		LIST_REMOVE(dev, si_siblings);
395 		dev->si_flags &= ~SI_CHILD;
396 	}
397 	while (!LIST_EMPTY(&dev->si_children))
398 		destroy_dev(LIST_FIRST(&dev->si_children));
399 	dev->si_drv1 = 0;
400 	dev->si_drv2 = 0;
401 	dev->si_devsw = 0;
402 	bzero(&dev->__si_u, sizeof(dev->__si_u));
403 	dev->si_flags &= ~SI_NAMED;
404 	dev->si_flags &= ~SI_ALIAS;
405 	freedev(dev);
406 }
407 
408 const char *
409 devtoname(dev_t dev)
410 {
411 	char *p;
412 	int mynor;
413 
414 	if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') {
415 		p = dev->si_name;
416 		if (devsw(dev))
417 			sprintf(p, "#%s/", devsw(dev)->d_name);
418 		else
419 			sprintf(p, "#%d/", major(dev));
420 		p += strlen(p);
421 		mynor = minor(dev);
422 		if (mynor < 0 || mynor > 255)
423 			sprintf(p, "%#x", (u_int)mynor);
424 		else
425 			sprintf(p, "%d", mynor);
426 	}
427 	return (dev->si_name);
428 }
429 
430 int
431 dev_stdclone(char *name, char **namep, const char *stem, int *unit)
432 {
433 	int u, i;
434 
435 	i = strlen(stem);
436 	if (bcmp(stem, name, i) != 0)
437 		return (0);
438 	if (!isdigit(name[i]))
439 		return (0);
440 	u = 0;
441 	if (name[i] == '0' && isdigit(name[i+1]))
442 		return (0);
443 	while (isdigit(name[i])) {
444 		u *= 10;
445 		u += name[i++] - '0';
446 	}
447 	if (u > 0xffffff)
448 		return (0);
449 	*unit = u;
450 	if (namep)
451 		*namep = &name[i];
452 	if (name[i])
453 		return (2);
454 	return (1);
455 }
456 
457 /*
458  * Helper sysctl for devname(3).  We're given a {u}dev_t and return
459  * the name, if any, registered by the device driver.
460  */
461 static int
462 sysctl_devname(SYSCTL_HANDLER_ARGS)
463 {
464 	int error;
465 	udev_t ud;
466 	dev_t dev;
467 
468 	error = SYSCTL_IN(req, &ud, sizeof (ud));
469 	if (error)
470 		return (error);
471 	if (ud == NOUDEV)
472 		return(EINVAL);
473 	dev = makedev(umajor(ud), uminor(ud));
474 	if (dev->si_name[0] == '\0')
475 		error = ENOENT;
476 	else
477 		error = SYSCTL_OUT(req, dev->si_name, strlen(dev->si_name) + 1);
478 	freedev(dev);
479 	return (error);
480 }
481 
482 SYSCTL_PROC(_kern, OID_AUTO, devname, CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_ANYBODY,
483 	NULL, 0, sysctl_devname, "", "devname(3) handler");
484 
485 /*
486  * Set ready_for_devs; prior to this point, device creation is not allowed.
487  */
488 static void
489 dev_set_ready(void *junk)
490 {
491 	ready_for_devs = 1;
492 }
493 
494 SYSINIT(dev_ready, SI_SUB_DEVFS, SI_ORDER_FIRST, dev_set_ready, NULL);
495