]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/kern/tty_cons.c
Fix a panic unloading the bktr driver when devfs is in use.
[FreeBSD/FreeBSD.git] / sys / kern / tty_cons.c
1 /*
2  * Copyright (c) 1988 University of Utah.
3  * Copyright (c) 1991 The Regents of the University of California.
4  * All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * the Systems Programming Group of the University of Utah Computer
8  * Science Department.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *      This product includes software developed by the University of
21  *      California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  *      from: @(#)cons.c        7.2 (Berkeley) 5/9/91
39  * $FreeBSD$
40  */
41
42 #include "opt_ddb.h"
43
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/conf.h>
47 #include <sys/cons.h>
48 #include <sys/fcntl.h>
49 #include <sys/kernel.h>
50 #include <sys/malloc.h>
51 #include <sys/namei.h>
52 #include <sys/proc.h>
53 #include <sys/queue.h>
54 #include <sys/reboot.h>
55 #include <sys/sysctl.h>
56 #include <sys/tty.h>
57 #include <sys/uio.h>
58 #include <sys/vnode.h>
59
60 #include <ddb/ddb.h>
61
62 #include <machine/cpu.h>
63
64 static  d_open_t        cnopen;
65 static  d_close_t       cnclose;
66 static  d_read_t        cnread;
67 static  d_write_t       cnwrite;
68 static  d_ioctl_t       cnioctl;
69 static  d_poll_t        cnpoll;
70 static  d_kqfilter_t    cnkqfilter;
71
72 #define CDEV_MAJOR      0
73 static struct cdevsw cn_cdevsw = {
74         /* open */      cnopen,
75         /* close */     cnclose,
76         /* read */      cnread,
77         /* write */     cnwrite,
78         /* ioctl */     cnioctl,
79         /* poll */      cnpoll,
80         /* mmap */      nommap,
81         /* strategy */  nostrategy,
82         /* name */      "console",
83         /* maj */       CDEV_MAJOR,
84         /* dump */      nodump,
85         /* psize */     nopsize,
86         /* flags */     D_TTY | D_KQFILTER,
87         /* kqfilter */  cnkqfilter,
88 };
89
90 struct cn_device {
91         STAILQ_ENTRY(cn_device) cnd_next;
92         char            cnd_name[16];
93         struct          vnode *cnd_vp;
94         struct          consdev *cnd_cn;
95 };
96
97 #define CNDEVPATHMAX    32
98 #define CNDEVTAB_SIZE   4
99 static struct cn_device cn_devtab[CNDEVTAB_SIZE];
100 static STAILQ_HEAD(, cn_device) cn_devlist =
101     STAILQ_HEAD_INITIALIZER(cn_devlist);
102
103 #define CND_INVALID(cnd, td)                                            \
104         (cnd == NULL || cnd->cnd_vp == NULL ||                          \
105             (cnd->cnd_vp->v_type == VBAD && !cn_devopen(cnd, td, 1)))
106
107 static udev_t   cn_udev_t;
108 SYSCTL_OPAQUE(_machdep, CPU_CONSDEV, consdev, CTLFLAG_RD,
109         &cn_udev_t, sizeof cn_udev_t, "T,dev_t", "");
110
111 int     cons_unavail = 0;       /* XXX:
112                                  * physical console not available for
113                                  * input (i.e., it is in graphics mode)
114                                  */
115 static int cn_mute;
116 static int openflag;                    /* how /dev/console was opened */
117 static int cn_is_open;
118 static dev_t cn_devfsdev;               /* represents the device private info */
119 static u_char console_pausing;          /* pause after each line during probe */
120 static char *console_pausestr=
121 "<pause; press any key to proceed to next line or '.' to end pause mode>";
122
123 void    cndebug(char *);
124
125 CONS_DRIVER(cons, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
126 SET_DECLARE(cons_set, struct consdev);
127
128 void
129 cninit(void)
130 {
131         struct consdev *best_cn, *cn, **list;
132
133         /*
134          * Check if we should mute the console (for security reasons perhaps)
135          * It can be changes dynamically using sysctl kern.consmute
136          * once we are up and going.
137          * 
138          */
139         cn_mute = ((boothowto & (RB_MUTE
140                         |RB_SINGLE
141                         |RB_VERBOSE
142                         |RB_ASKNAME
143                         |RB_CONFIG)) == RB_MUTE);
144
145         /*
146          * Find the first console with the highest priority.
147          */
148         best_cn = NULL;
149         SET_FOREACH(list, cons_set) {
150                 cn = *list;
151                 cnremove(cn);
152                 if (cn->cn_probe == NULL)
153                         continue;
154                 cn->cn_probe(cn);
155                 if (cn->cn_pri == CN_DEAD)
156                         continue;
157                 if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri)
158                         best_cn = cn;
159                 if (boothowto & RB_MULTIPLE) {
160                         /*
161                          * Initialize console, and attach to it.
162                          */
163                         cnadd(cn);
164                         cn->cn_init(cn);
165                 }
166         }
167         if (best_cn == NULL)
168                 return;
169         if ((boothowto & RB_MULTIPLE) == 0) {
170                 cnadd(best_cn);
171                 best_cn->cn_init(best_cn);
172         }
173         if (boothowto & RB_PAUSE)
174                 console_pausing = 1;
175         /*
176          * Make the best console the preferred console.
177          */
178         cnselect(best_cn);
179 }
180
181 void
182 cninit_finish()
183 {
184         console_pausing = 0;
185
186
187 /* add a new physical console to back the virtual console */
188 int
189 cnadd(struct consdev *cn)
190 {
191         struct cn_device *cnd;
192         int i;
193
194         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
195                 if (cnd->cnd_cn == cn)
196                         return (0);
197         for (i = 0; i < CNDEVTAB_SIZE; i++) {
198                 cnd = &cn_devtab[i];
199                 if (cnd->cnd_cn == NULL)
200                         break;
201         }
202         if (cnd->cnd_cn != NULL)
203                 return (ENOMEM);
204         cnd->cnd_cn = cn;
205         STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next);
206         return (0);
207 }
208
209 void
210 cnremove(struct consdev *cn)
211 {
212         struct cn_device *cnd;
213
214         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
215                 if (cnd->cnd_cn != cn)
216                         continue;
217                 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
218                 if (cnd->cnd_vp != NULL)
219                         vn_close(cnd->cnd_vp, openflag, NOCRED, NULL);
220                 cnd->cnd_vp = NULL;
221                 cnd->cnd_cn = NULL;
222                 cnd->cnd_name[0] = '\0';
223 #if 0
224                 /*
225                  * XXX
226                  * syscons gets really confused if console resources are
227                  * freed after the system has initialized.
228                  */
229                 if (cn->cn_term != NULL)
230                         cn->cn_term(cn);
231 #endif
232                 return;
233         }
234 }
235
236 void
237 cnselect(struct consdev *cn)
238 {
239         struct cn_device *cnd;
240
241         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
242                 if (cnd->cnd_cn != cn)
243                         continue;
244                 if (cnd == STAILQ_FIRST(&cn_devlist))
245                         return;
246                 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
247                 STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next);
248                 return;
249         }
250 }
251
252 void
253 cndebug(char *str)
254 {
255         int i, len;
256
257         len = strlen(str);
258         cnputc('>'); cnputc('>'); cnputc('>'); cnputc(' '); 
259         for (i = 0; i < len; i++)
260                 cnputc(str[i]);
261         cnputc('\n');
262 }
263
264 static int
265 sysctl_kern_console(SYSCTL_HANDLER_ARGS)
266 {
267         struct cn_device *cnd;
268         struct consdev *cp, **list;
269         char *name, *p;
270         int delete, len, error;
271
272         len = 2;
273         SET_FOREACH(list, cons_set) {
274                 cp = *list;
275                 if (cp->cn_dev != NULL)
276                         len += strlen(devtoname(cp->cn_dev)) + 1;
277         }
278         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
279                 len += strlen(devtoname(cnd->cnd_cn->cn_dev)) + 1;
280         len = len > CNDEVPATHMAX ? len : CNDEVPATHMAX;
281         MALLOC(name, char *, len, M_TEMP, M_WAITOK | M_ZERO);
282         p = name;
283         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
284                 p += sprintf(p, "%s,", devtoname(cnd->cnd_cn->cn_dev));
285         *p++ = '/';
286         SET_FOREACH(list, cons_set) {
287                 cp = *list;
288                 if (cp->cn_dev != NULL)
289                         p += sprintf(p, "%s,", devtoname(cp->cn_dev));
290         }
291         error = sysctl_handle_string(oidp, name, len, req);
292         if (error == 0 && req->newptr != NULL) {
293                 p = name;
294                 error = ENXIO;
295                 delete = 0;
296                 if (*p == '-') {
297                         delete = 1;
298                         p++;
299                 }
300                 SET_FOREACH(list, cons_set) {
301                         cp = *list;
302                         if (cp->cn_dev == NULL ||
303                             strcmp(p, devtoname(cp->cn_dev)) != 0)
304                                 continue;
305                         if (delete) {
306                                 cnremove(cp);
307                                 error = 0;
308                         } else {
309                                 error = cnadd(cp);
310                                 if (error == 0)
311                                         cnselect(cp);
312                         }
313                         break;
314                 }
315         }
316         FREE(name, M_TEMP);
317         return (error);
318 }
319
320 SYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW,
321         0, 0, sysctl_kern_console, "A", "Console device control");
322
323 /*
324  * User has changed the state of the console muting.
325  * This may require us to open or close the device in question.
326  */
327 static int
328 sysctl_kern_consmute(SYSCTL_HANDLER_ARGS)
329 {
330         int error;
331         int ocn_mute;
332
333         ocn_mute = cn_mute;
334         error = sysctl_handle_int(oidp, &cn_mute, 0, req);
335         if (error != 0 || req->newptr == NULL)
336                 return (error);
337         if (ocn_mute && !cn_mute && cn_is_open)
338                 error = cnopen(NODEV, openflag, 0, curthread);
339         else if (!ocn_mute && cn_mute && cn_is_open) {
340                 error = cnclose(NODEV, openflag, 0, curthread);
341                 cn_is_open = 1;         /* XXX hack */
342         }
343         return (error);
344 }
345
346 SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
347         0, sizeof(cn_mute), sysctl_kern_consmute, "I", "");
348
349 static int
350 cn_devopen(struct cn_device *cnd, struct thread *td, int forceopen)
351 {
352         char path[CNDEVPATHMAX];
353         struct nameidata nd;
354         struct vnode *vp;
355         dev_t dev;
356         int error;
357
358         if ((vp = cnd->cnd_vp) != NULL) {
359                 if (!forceopen && vp->v_type != VBAD) {
360                         dev = vp->v_rdev;
361                         return ((*devsw(dev)->d_open)(dev, openflag, 0, td));
362                 }
363                 cnd->cnd_vp = NULL;
364                 vn_close(vp, openflag, td->td_ucred, td);
365         }
366         if (cnd->cnd_name[0] == '\0') {
367                 strlcpy(cnd->cnd_name, devtoname(cnd->cnd_cn->cn_dev),
368                     sizeof(cnd->cnd_name));
369         }
370         snprintf(path, sizeof(path), "/dev/%s", cnd->cnd_name);
371         NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, td);
372         error = vn_open(&nd, &openflag, 0);
373         if (error == 0) {
374                 NDFREE(&nd, NDF_ONLY_PNBUF);
375                 VOP_UNLOCK(nd.ni_vp, 0, td);
376                 if (nd.ni_vp->v_type == VCHR)
377                         cnd->cnd_vp = nd.ni_vp;
378                 else
379                         vn_close(nd.ni_vp, openflag, td->td_ucred, td);
380         }
381         return (cnd->cnd_vp != NULL);
382 }
383
384 static int
385 cnopen(dev_t dev, int flag, int mode, struct thread *td)
386 {
387         struct cn_device *cnd;
388
389         openflag = flag | FWRITE;       /* XXX */
390         cn_is_open = 1;                 /* console is logically open */
391         if (cn_mute)
392                 return (0);
393         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
394                 cn_devopen(cnd, td, 0);
395         return (0);
396 }
397
398 static int
399 cnclose(dev_t dev, int flag, int mode, struct thread *td)
400 {
401         struct cn_device *cnd;
402         struct vnode *vp;
403
404         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
405                 if ((vp = cnd->cnd_vp) == NULL)
406                         continue; 
407                 cnd->cnd_vp = NULL;
408                 vn_close(vp, openflag, td->td_ucred, td);
409         }
410         cn_is_open = 0;
411         return (0);
412 }
413
414 static int
415 cnread(dev_t dev, struct uio *uio, int flag)
416 {
417         struct cn_device *cnd;
418
419         cnd = STAILQ_FIRST(&cn_devlist);
420         if (cn_mute || CND_INVALID(cnd, curthread))
421                 return (0);
422         dev = cnd->cnd_vp->v_rdev;
423         return ((*devsw(dev)->d_read)(dev, uio, flag));
424 }
425
426 static int
427 cnwrite(dev_t dev, struct uio *uio, int flag)
428 {
429         struct cn_device *cnd;
430
431         cnd = STAILQ_FIRST(&cn_devlist);
432         if (cn_mute || CND_INVALID(cnd, curthread))
433                 goto done;
434         if (constty)
435                 dev = constty->t_dev;
436         else
437                 dev = cnd->cnd_vp->v_rdev;
438         if (dev != NULL) {
439                 log_console(uio);
440                 return ((*devsw(dev)->d_write)(dev, uio, flag));
441         }
442 done:
443         uio->uio_resid = 0; /* dump the data */
444         return (0);
445 }
446
447 static int
448 cnioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
449 {
450         struct cn_device *cnd;
451         int error;
452
453         cnd = STAILQ_FIRST(&cn_devlist);
454         if (cn_mute || CND_INVALID(cnd, td))
455                 return (0);
456         /*
457          * Superuser can always use this to wrest control of console
458          * output from the "virtual" console.
459          */
460         if (cmd == TIOCCONS && constty) {
461                 error = suser(td);
462                 if (error)
463                         return (error);
464                 constty = NULL;
465                 return (0);
466         }
467         dev = cnd->cnd_vp->v_rdev;
468         if (dev != NULL)
469                 return ((*devsw(dev)->d_ioctl)(dev, cmd, data, flag, td));
470         return (0);
471 }
472
473 /*
474  * XXX
475  * poll/kqfilter do not appear to be correct
476  */
477 static int
478 cnpoll(dev_t dev, int events, struct thread *td)
479 {
480         struct cn_device *cnd;
481
482         cnd = STAILQ_FIRST(&cn_devlist);
483         if (cn_mute || CND_INVALID(cnd, td))
484                 return (0);
485         dev = cnd->cnd_vp->v_rdev;
486         if (dev != NULL)
487                 return ((*devsw(dev)->d_poll)(dev, events, td));
488         return (0);
489 }
490
491 static int
492 cnkqfilter(dev_t dev, struct knote *kn)
493 {
494         struct cn_device *cnd;
495
496         cnd = STAILQ_FIRST(&cn_devlist);
497         if (cn_mute || CND_INVALID(cnd, curthread))
498                 return (1);
499         dev = cnd->cnd_vp->v_rdev;
500         if (dev != NULL)
501                 return ((*devsw(dev)->d_kqfilter)(dev, kn));
502         return (1);
503 }
504
505 /*
506  * Low level console routines.
507  */
508 int
509 cngetc(void)
510 {
511         int c;
512
513         if (cn_mute)
514                 return (-1);
515         while ((c = cncheckc()) == -1)
516                 ;
517         if (c == '\r')
518                 c = '\n';               /* console input is always ICRNL */
519         return (c);
520 }
521
522 int
523 cncheckc(void)
524 {
525         struct cn_device *cnd;
526         struct consdev *cn;
527         int c;
528
529         if (cn_mute)
530                 return (-1);
531         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
532                 cn = cnd->cnd_cn;
533                 c = cn->cn_checkc(cn->cn_dev);
534                 if (c != -1) {
535                         return (c);
536                 }
537         }
538         return (-1);
539 }
540
541 void
542 cnputc(int c)
543 {
544         struct cn_device *cnd;
545         struct consdev *cn;
546         char *cp;
547
548         if (cn_mute || c == '\0')
549                 return;
550         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
551                 cn = cnd->cnd_cn;
552                 if (c == '\n')
553                         cn->cn_putc(cn->cn_dev, '\r');
554                 cn->cn_putc(cn->cn_dev, c);
555         }
556 #ifdef DDB
557         if (console_pausing && !db_active && (c == '\n')) {
558 #else
559         if (console_pausing && (c == '\n')) {
560 #endif
561                 for (cp = console_pausestr; *cp != '\0'; cp++)
562                         cnputc(*cp);
563                 if (cngetc() == '.')
564                         console_pausing = 0;
565                 cnputc('\r');
566                 for (cp = console_pausestr; *cp != '\0'; cp++)
567                         cnputc(' ');
568                 cnputc('\r');
569         }
570 }
571
572 void
573 cndbctl(int on)
574 {
575         struct cn_device *cnd;
576         struct consdev *cn;
577         static int refcount;
578
579         if (!on)
580                 refcount--;
581         if (refcount == 0)
582                 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
583                         cn = cnd->cnd_cn;
584                         if (cn->cn_dbctl != NULL)
585                                 cn->cn_dbctl(cn->cn_dev, on);
586                 }
587         if (on)
588                 refcount++;
589 }
590
591 static void
592 cn_drvinit(void *unused)
593 {
594
595         cn_devfsdev = make_dev(&cn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
596             "console");
597 }
598
599 SYSINIT(cndev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,cn_drvinit,NULL)