Correct BSD License clause numbering from 1-2-4 to 1-2-3.
[dragonfly.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. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  *      from: @(#)cons.c        7.2 (Berkeley) 5/9/91
35  * $FreeBSD: src/sys/kern/tty_cons.c,v 1.81.2.4 2001/12/17 18:44:41 guido Exp $
36  */
37
38 #include "opt_ddb.h"
39 #include "opt_comconsole.h"
40
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/conf.h>
44 #include <sys/cons.h>
45 #include <sys/kernel.h>
46 #include <sys/proc.h>
47 #include <sys/priv.h>
48 #include <sys/reboot.h>
49 #include <sys/sysctl.h>
50 #include <sys/tty.h>
51 #include <sys/uio.h>
52 #include <sys/msgport.h>
53 #include <sys/msgport2.h>
54 #include <sys/device.h>
55
56 #include <ddb/ddb.h>
57
58 #include <machine/cpu.h>
59
60 static d_open_t cnopen;
61 static d_close_t cnclose;
62 static d_read_t cnread;
63 static d_write_t cnwrite;
64 static d_ioctl_t cnioctl;
65 static d_kqfilter_t cnkqfilter;
66
67 static int cnintercept(struct dev_generic_args *ap);
68
69 #define CDEV_MAJOR      0
70 static struct dev_ops cn_ops = {
71         { "console", 0, D_TTY },
72         .d_open =       cnopen,
73         .d_close =      cnclose,
74         .d_read =       cnread,
75         .d_write =      cnwrite,
76         .d_ioctl =      cnioctl,
77         .d_kqfilter =   cnkqfilter,
78 };
79
80 static struct dev_ops cn_iops = {
81         { "intercept", 0, D_TTY },
82         .d_default =    cnintercept
83 };
84
85 static struct dev_ops *cn_fwd_ops;
86 static cdev_t   cn_dev;
87
88 //XXX: get this shit out! (alexh)
89 #if 0
90 SYSCTL_OPAQUE(_machdep, CPU_CONSDEV, consdev, CTLFLAG_RD,
91         &cn_udev, sizeof cn_udev, "T,udev_t", "");
92 #endif
93
94 static int cn_mute;
95
96 #ifdef BREAK_TO_DEBUGGER
97 int     break_to_debugger = 1;
98 #else
99 int     break_to_debugger = 0;
100 #endif
101 TUNABLE_INT("kern.break_to_debugger", &break_to_debugger);
102 SYSCTL_INT(_kern, OID_AUTO, break_to_debugger, CTLFLAG_RW,
103         &break_to_debugger, 0, "");
104
105 #ifdef ALT_BREAK_TO_DEBUGGER
106 int     alt_break_to_debugger = 1;
107 #else
108 int     alt_break_to_debugger = 0;
109 #endif
110 TUNABLE_INT("kern.alt_break_to_debugger", &alt_break_to_debugger);
111 SYSCTL_INT(_kern, OID_AUTO, alt_break_to_debugger, CTLFLAG_RW,
112         &alt_break_to_debugger, 0, "");
113
114 int     cons_unavail = 0;       /* XXX:
115                                  * physical console not available for
116                                  * input (i.e., it is in graphics mode)
117                                  */
118 int     sysbeep_enable = 1;
119
120 static u_char cn_is_open;               /* nonzero if logical console is open */
121 static int openmode, openflag;          /* how /dev/console was openned */
122 static cdev_t cn_devfsdev;              /* represents the device private info */
123 static u_char cn_phys_is_open;          /* nonzero if physical device is open */
124 static u_char console_pausing;          /* pause after each line during probe */
125 static char *console_pausestr=
126 "<pause; press any key to proceed to next line or '.' to end pause mode>";
127
128 struct consdev *cn_tab;         /* physical console device info */
129 struct consdev *gdb_tab;        /* physical gdb debugger device info */
130
131 SYSCTL_INT(_kern, OID_AUTO, sysbeep_enable, CTLFLAG_RW, &sysbeep_enable, 0, "");
132
133 CONS_DRIVER(cons, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
134 SET_DECLARE(cons_set, struct consdev);
135
136 void
137 cninit(void)
138 {
139         struct consdev *best_cp, *cp, **list;
140
141         /*
142          * Workaround for token acquisition and releases during the
143          * console init.  For some reason if lwkt_gettoken()'s mpcount
144          * optimization is turned off the console init blows up.  It
145          * might be trying to kprintf() something in the middle of
146          * its init.
147          */
148         lwkt_gettoken(&tty_token);
149
150         /*
151          * Check if we should mute the console (for security reasons perhaps)
152          * It can be changes dynamically using sysctl kern.consmute
153          * once we are up and going.
154          *
155          */
156         cn_mute = ((boothowto & (RB_MUTE
157                         |RB_SINGLE
158                         |RB_VERBOSE
159                         |RB_ASKNAME
160                         |RB_CONFIG)) == RB_MUTE);
161
162         /*
163          * Find the first console with the highest priority.
164          */
165         best_cp = NULL;
166         SET_FOREACH(list, cons_set) {
167                 cp = *list;
168                 if (cp->cn_probe == NULL)
169                         continue;
170                 (*cp->cn_probe)(cp);
171                 if (cp->cn_pri > CN_DEAD && cp->cn_probegood &&
172                     (best_cp == NULL || cp->cn_pri > best_cp->cn_pri))
173                         best_cp = cp;
174         }
175
176         
177         /*
178          * If no console, give up.
179          */
180         if (best_cp == NULL) {
181                 if (cn_tab != NULL && cn_tab->cn_term != NULL)
182                         (*cn_tab->cn_term)(cn_tab);
183                 goto done;
184         }
185
186         /*
187          * Initialize console, then attach to it.  This ordering allows
188          * debugging using the previous console, if any.
189          */
190         (*best_cp->cn_init)(best_cp);
191         if (cn_tab != NULL && cn_tab != best_cp) {
192                 /* Turn off the previous console.  */
193                 if (cn_tab->cn_term != NULL)
194                         (*cn_tab->cn_term)(cn_tab);
195         }
196         if (boothowto & RB_PAUSE)
197                 console_pausing = 1;
198 done:
199         cn_tab = best_cp;
200
201         /*
202          * We can safely release the token after the init is done.
203          * Also assert that the mpcount is still correct or otherwise
204          * the SMP/AP boot will blow up on us.
205          */
206         lwkt_reltoken(&tty_token);
207 }
208
209
210 /*
211  * Hook the open and close functions on the selected device.
212  */
213 void
214 cninit_finish(void)
215 {
216         if ((cn_tab == NULL) || cn_mute)
217                 return;
218         if (cn_tab->cn_dev == NULL) {
219                 cn_tab->cn_init_fini(cn_tab);
220                 if (cn_tab->cn_dev == NULL) {
221                         kprintf("Unable to hook console! cn_tab %p\n", cn_tab);
222                         return;
223                 }
224         }
225
226         cn_fwd_ops = dev_ops_intercept(cn_tab->cn_dev, &cn_iops);
227         cn_dev = cn_tab->cn_dev;
228         console_pausing = 0;
229 }
230
231 static void
232 cnuninit(void)
233 {
234         if (cn_tab == NULL)
235                 return;
236         if (cn_fwd_ops)
237                 dev_ops_restore(cn_tab->cn_dev, cn_fwd_ops);
238         cn_fwd_ops = NULL;
239         cn_dev = NULL;
240 }
241
242
243 /*
244  * User has changed the state of the console muting.
245  * This may require us to open or close the device in question.
246  */
247 static int
248 sysctl_kern_consmute(SYSCTL_HANDLER_ARGS)
249 {
250         int error;
251         int ocn_mute;
252
253         ocn_mute = cn_mute;
254         error = sysctl_handle_int(oidp, &cn_mute, 0, req);
255         if((error == 0) && (cn_tab != NULL) && (req->newptr != NULL)) {
256                 if(ocn_mute && !cn_mute) {
257                         /*
258                          * going from muted to unmuted.. open the physical dev 
259                          * if the console has been openned
260                          */
261                         cninit_finish();
262                         if (cn_is_open) {
263                                 /* XXX curproc is not what we want really */
264                                 error = dev_dopen(cn_dev, openflag,
265                                                 openmode, curproc->p_ucred);
266                         }
267                         /* if it failed, back it out */
268                         if ( error != 0) cnuninit();
269                 } else if (!ocn_mute && cn_mute) {
270                         /*
271                          * going from unmuted to muted.. close the physical dev 
272                          * if it's only open via /dev/console
273                          */
274                         if (cn_is_open) {
275                                 error = dev_dclose(cn_dev, openflag,
276                                                    openmode);
277                         }
278                         if (error == 0)
279                                 cnuninit();
280                 }
281                 if (error != 0) {
282                         /* 
283                          * back out the change if there was an error
284                          */
285                         cn_mute = ocn_mute;
286                 }
287         }
288         return (error);
289 }
290
291 SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
292         0, sizeof cn_mute, sysctl_kern_consmute, "I", "");
293
294
295 /*
296  * We intercept the OPEN and CLOSE calls on the original device, and
297  * forward the rest through.
298  */
299 static int
300 cnintercept(struct dev_generic_args *ap)
301 {
302         int error;
303
304         if (ap->a_desc == &dev_open_desc) {
305                 error = cnopen((struct dev_open_args *)ap);
306         } else if (ap->a_desc == &dev_close_desc) {
307                 error = cnclose((struct dev_close_args *)ap);
308         } else if (cn_fwd_ops) {
309                 error = dev_doperate_ops(cn_fwd_ops, ap);
310         } else {
311                 error = ENXIO;
312         }
313         return (error);
314 }
315
316 /*
317  * cnopen() is called as an intercept function (dev will be that of the
318  * actual physical device representing our console), and also called from
319  * the muting code and from the /dev/console switch (dev will have the
320  * console's cdevsw).
321  */
322 static int
323 cnopen(struct dev_open_args *ap)
324 {
325         cdev_t dev = ap->a_head.a_dev;
326         int flag = ap->a_oflags;
327         int mode = ap->a_devtype;
328         cdev_t cndev;
329         cdev_t physdev;
330         int retval = 0;
331
332         if (cn_tab == NULL || cn_fwd_ops == NULL)
333                 return (0);
334
335         cndev = cn_tab->cn_dev;         /* actual physical device */
336         physdev = (dev == cn_devfsdev) ? cndev : dev;
337
338         /*
339          * If mute is active, then non console opens don't get here
340          * so we don't need to check for that. They bypass this and go
341          * straight to the device.
342          *
343          * It is important to note that due to our intercept and the fact
344          * that we might be called via the original (intercepted) device,
345          * the original device's ops may point to us, so to avoid an
346          * infinite recursion we have to forward through cn_fwd_ops.
347          * This is a severe hack that really needs to be fixed XXX.
348          *
349          * XXX at the moment we assume that the port forwarding function
350          * is synchronous for open.
351          */
352         if (!cn_mute) {
353                 ap->a_head.a_dev = physdev;
354                 retval = dev_doperate_ops(cn_fwd_ops, &ap->a_head);
355         }
356         if (retval == 0) {
357                 /*
358                  * check if we openned it via /dev/console or
359                  * via the physical entry (e.g. /dev/sio0).
360                  */
361                 if (dev == cndev) {
362                         cn_phys_is_open = 1;
363                 } else if (physdev == cndev) {
364                         openmode = mode;
365                         openflag = flag;
366                         cn_is_open = 1;
367                 }
368                 dev->si_tty = cndev->si_tty;
369         }
370         return (retval);
371 }
372
373 /*
374  * cnclose() is called as a port intercept function (dev will be that of the
375  * actual physical device representing our console), and also called from
376  * the muting code and from the /dev/console switch (dev will have the
377  * console's cdevsw).
378  */
379 static int
380 cnclose(struct dev_close_args *ap)
381 {
382         struct tty *cn_tp;
383         cdev_t cndev;
384         cdev_t physdev;
385         cdev_t dev = ap->a_head.a_dev;
386
387         if (cn_tab == NULL || cn_fwd_ops == NULL)
388                 return(0);
389         cndev = cn_tab->cn_dev;
390         cn_tp = cndev->si_tty;
391         physdev = (dev == cn_devfsdev) ? cndev : dev;
392
393         /*
394          * act appropriatly depending on whether it's /dev/console
395          * or the pysical device (e.g. /dev/sio) that's being closed.
396          * in either case, don't actually close the device unless
397          * both are closed.
398          */
399         if (dev == cndev) {
400                 /* the physical device is about to be closed */
401                 cn_phys_is_open = 0;
402                 if (cn_is_open) {
403                         if (cn_tp) {
404                                 /* perform a ttyhalfclose() */
405                                 /* reset session and proc group */
406                                 ttyclearsession(cn_tp);
407                         }
408                         return(0);
409                 }
410         } else if (physdev == cndev) {
411                 /* the logical console is about to be closed */
412                 cn_is_open = 0;
413                 if (cn_phys_is_open)
414                         return(0);
415                 dev = cndev;
416         }
417         if (cn_fwd_ops) {
418                 ap->a_head.a_dev = dev;
419                 return (dev_doperate_ops(cn_fwd_ops, &ap->a_head));
420         }
421         return (0);
422 }
423
424 /*
425  * The following functions are dispatched solely from the /dev/console
426  * device.  Their job is primarily to forward the request through.
427  * If the console is not attached to anything then write()'s are sunk
428  * to null and reads return 0 (mostly).
429  */
430 static int
431 cnread(struct dev_read_args *ap)
432 {
433         if (cn_tab == NULL || cn_fwd_ops == NULL)
434                 return (0);
435         ap->a_head.a_dev = cn_tab->cn_dev;
436         return (dev_doperate(&ap->a_head));
437 }
438
439 static int
440 cnwrite(struct dev_write_args *ap)
441 {
442         struct uio *uio = ap->a_uio;
443         cdev_t dev;
444
445         if (cn_tab == NULL || cn_fwd_ops == NULL) {
446                 uio->uio_resid = 0; /* dump the data */
447                 return (0);
448         }
449         if (constty)
450                 dev = constty->t_dev;
451         else
452                 dev = cn_tab->cn_dev;
453         log_console(uio);
454         ap->a_head.a_dev = dev;
455         return (dev_doperate(&ap->a_head));
456 }
457
458 static int
459 cnioctl(struct dev_ioctl_args *ap)
460 {
461         int error;
462
463         if (cn_tab == NULL || cn_fwd_ops == NULL)
464                 return (0);
465         KKASSERT(curproc != NULL);
466         /*
467          * Superuser can always use this to wrest control of console
468          * output from the "virtual" console.
469          */
470         if (ap->a_cmd == TIOCCONS && constty) {
471                 if (ap->a_cred) {
472                         error = priv_check_cred(ap->a_cred, PRIV_ROOT, 0);
473                         if (error)
474                                 return (error);
475                 }
476                 constty = NULL;
477                 return (0);
478         }
479         ap->a_head.a_dev = cn_tab->cn_dev;
480         return (dev_doperate(&ap->a_head));
481 }
482
483 static int
484 cnkqfilter(struct dev_kqfilter_args *ap)
485 {
486         if ((cn_tab == NULL) || cn_mute || cn_fwd_ops == NULL)
487                 return (1);
488         ap->a_head.a_dev = cn_tab->cn_dev;
489         return (dev_doperate(&ap->a_head));
490 }
491
492 /*
493  * These synchronous functions are primarily used the kernel needs to 
494  * access the keyboard (e.g. when running the debugger), or output data
495  * directly to the console.
496  */
497 int
498 cngetc(void)
499 {
500         int c;
501         if ((cn_tab == NULL) || cn_mute)
502                 return (-1);
503         c = (*cn_tab->cn_getc)(cn_tab->cn_private);
504         if (c == '\r') c = '\n'; /* console input is always ICRNL */
505         return (c);
506 }
507
508 int
509 cncheckc(void)
510 {
511         if ((cn_tab == NULL) || cn_mute)
512                 return (-1);
513         return ((*cn_tab->cn_checkc)(cn_tab->cn_private));
514 }
515
516 void
517 cnputc(int c)
518 {
519         char *cp;
520
521         if ((cn_tab == NULL) || cn_mute)
522                 return;
523         if (c) {
524                 if (c == '\n')
525                         (*cn_tab->cn_putc)(cn_tab->cn_private, '\r');
526                 (*cn_tab->cn_putc)(cn_tab->cn_private, c);
527 #ifdef DDB
528                 if (console_pausing && !db_active && (c == '\n')) {
529 #else
530                 if (console_pausing && (c == '\n')) {
531 #endif
532                         for(cp=console_pausestr; *cp != '\0'; cp++)
533                             (*cn_tab->cn_putc)(cn_tab->cn_private, *cp);
534                         if (cngetc() == '.')
535                                 console_pausing = 0;
536                         (*cn_tab->cn_putc)(cn_tab->cn_private, '\r');
537                         for(cp=console_pausestr; *cp != '\0'; cp++)
538                             (*cn_tab->cn_putc)(cn_tab->cn_private, ' ');
539                         (*cn_tab->cn_putc)(cn_tab->cn_private, '\r');
540                 }
541         }
542 }
543
544 void
545 cndbctl(int on)
546 {
547         static int refcount;
548
549         if (cn_tab == NULL)
550                 return;
551         if (!on)
552                 refcount--;
553         if (refcount == 0 && cn_tab->cn_dbctl != NULL)
554                 (*cn_tab->cn_dbctl)(cn_tab->cn_private, on);
555         if (on)
556                 refcount++;
557 }
558
559 static void
560 cn_drvinit(void *unused)
561 {
562         cn_devfsdev = make_only_devfs_dev(&cn_ops, 0, UID_ROOT, GID_WHEEL,
563                                           0600, "console");
564 }
565
566 SYSINIT(cndev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,cn_drvinit,NULL)