4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <sys/types.h>
28 #include <sys/varargs.h>
29 #include <sys/modctl.h>
30 #include <sys/cmn_err.h>
31 #include <sys/console.h>
32 #include <sys/consdev.h>
33 #include <sys/promif.h>
35 #include <sys/polled_io.h>
36 #include <sys/systm.h>
40 #include <sys/taskq.h>
43 #include <sys/sunddi.h>
44 #include <sys/esunddi.h>
45 #include <sys/fs/snode.h>
46 #include <sys/termios.h>
47 #include <sys/tem_impl.h>
51 #define LOSCREENLINES 34
52 #define HISCREENLINES 48
56 #define LOSCREENCOLS 80
57 #define HISCREENCOLS 120
59 vnode_t
*console_vnode
;
60 taskq_t
*console_taskq
;
63 * The current set of polled I/O routines (if any)
65 struct cons_polledio
*cons_polledio
;
68 * Console I/O Routines
70 * In the event that kernel messages are generated with cmn_err(9F) or printf()
71 * early in boot, after a panic, in resource-constrained situations, or sent
72 * through /dev/console to the wscons driver, we may be called upon to render
73 * characters directly to the frame buffer using the underlying prom_*()
74 * routines. These in turn may attempt to use PROM services directly, or may
75 * use a kernel console emulator if one is available. Unfortunately, if PROM
76 * services are being used by the kernel on a multi-CPU system, these routines
77 * might be called while another CPU is simultaneously accessing a frame buffer
78 * memory mapping (perhaps through the X server). This situation may not be
79 * supported by the frame buffer hardware.
81 * To handle this situation, we implement a two-phase locking scheme which we
82 * use to protect accesses to the underlying prom_*() rendering routines. The
83 * common-code functions console_hold() and console_rele() are used to gain
84 * exclusive access to the console from within the kernel. We use a standard
85 * r/w lock in writer-mode only to implement the kernel lock. We use an r/w
86 * lock instead of a mutex here because character rendering is slow and hold
87 * times will be relatively long, and there is no point in adaptively spinning.
88 * These routines may be called recursively, in which case subsequent calls
89 * just increment the console_depth hold count. Once exclusive access is
90 * gained, we grab the frame buffer device node and block further mappings to
91 * it by holding the specfs node lock and the device node's lock. We then
92 * observe if any mappings are present by examining the specfs node's s_mapcnt
93 * (non-clone mmaps) and the devinfo node's devi_ref count (clone opens).
95 * Then, around each character rendering call, the routines console_enter()
96 * and console_exit() are used to inform the platform code that we are
97 * accessing the character rendering routines. These platform routines can
98 * then examine the "busy" flag returned by console_enter() and briefly stop
99 * the other CPUs so that they cannot access the frame buffer hardware while
100 * we are busy rendering characters. This mess can all be removed when the
101 * impossible dream of a unified kernel console emulator is someday realized.
104 static krwlock_t console_lock
;
105 static uint_t console_depth
;
106 static int console_busy
;
108 extern void pm_cfb_check_and_powerup(void);
109 extern void pm_cfb_rele(void);
114 if (panicstr
!= NULL
)
115 return (console_busy
); /* assume exclusive access in panic */
117 if (rw_owner(&console_lock
) != curthread
)
118 rw_enter(&console_lock
, RW_WRITER
);
120 if (console_depth
++ != 0)
121 return (console_busy
); /* lock is being entered recursively */
123 pm_cfb_check_and_powerup();
125 #ifdef _HAVE_TEM_FIRMWARE
126 if (consmode
== CONS_FW
&& ncpus
> 1 && fbvp
!= NULL
) {
127 struct snode
*csp
= VTOS(VTOS(fbvp
)->s_commonvp
);
129 mutex_enter(&csp
->s_lock
);
130 console_busy
= csp
->s_mapcnt
!= 0;
132 if (csp
->s_mapcnt
== 0 && fbdip
!= NULL
) {
133 mutex_enter(&DEVI(fbdip
)->devi_lock
);
134 console_busy
= DEVI(fbdip
)->devi_ref
!= 0;
137 #endif /* _HAVE_TEM_FIRMWARE */
138 return (console_busy
);
144 if (panicstr
!= NULL
)
145 return; /* do not modify lock states if we are panicking */
147 ASSERT(RW_WRITE_HELD(&console_lock
));
148 ASSERT(console_depth
!= 0);
150 if (--console_depth
!= 0)
151 return; /* lock is being dropped recursively */
153 #ifdef _HAVE_TEM_FIRMWARE
154 if (consmode
== CONS_FW
&& ncpus
> 1 && fbvp
!= NULL
) {
155 struct snode
*csp
= VTOS(VTOS(fbvp
)->s_commonvp
);
157 ASSERT(MUTEX_HELD(&csp
->s_lock
));
158 if (csp
->s_mapcnt
== 0 && fbdip
!= NULL
)
159 mutex_exit(&DEVI(fbdip
)->devi_lock
);
161 mutex_exit(&csp
->s_lock
);
163 #endif /* _HAVE_TEM_FIRMWARE */
166 rw_exit(&console_lock
);
170 console_getprop(dev_t dev
, dev_info_t
*dip
, char *name
, ushort_t
*sp
)
177 if (ddi_prop_lookup_byte_array(dev
, dip
, 0, name
, &data
, &len
) ==
179 for (i
= 0; i
< len
; i
++) {
180 if (data
[i
] < '0' || data
[i
] > '9')
182 *sp
= *sp
* 10 + data
[i
] - '0';
189 * Gets the number of rows and columns (in char's) and the
190 * width and height (in pixels) of the console.
193 console_get_size(ushort_t
*r
, ushort_t
*c
, ushort_t
*x
, ushort_t
*y
)
200 * If we have loaded the console IO stuff, then ask for the screen
201 * size properties from the layered terminal emulator. Else ask for
202 * them from the root node, which will eventually fall through to the
203 * options node and get them from the prom.
205 if (rwsconsvp
== NULL
|| consmode
== CONS_FW
) {
206 dip
= ddi_root_node();
209 dev
= rwsconsvp
->v_rdev
; /* layering is wc -> tem */
210 dip
= e_ddi_hold_devi_by_dev(dev
, 0);
215 * If we have not initialized a console yet and don't have a root
216 * node (ie. we have not initialized the DDI yet) return our default
217 * size for the screen.
226 console_getprop(DDI_DEV_T_ANY
, dip
, "screen-#columns", c
);
227 console_getprop(DDI_DEV_T_ANY
, dip
, "screen-#rows", r
);
228 console_getprop(DDI_DEV_T_ANY
, dip
, "screen-width", x
);
229 console_getprop(DDI_DEV_T_ANY
, dip
, "screen-height", y
);
233 else if (*c
> MAXCOLS
)
238 else if (*r
> MAXLINES
)
242 ddi_release_devi(dip
);
245 typedef struct console_msg
{
251 * If we can't access the console stream, fall through to PROM, which redirects
252 * it back into to terminal emulator as appropriate. The console stream should
253 * be available after consconfig runs.
256 console_putmsg(console_msg_t
*cm
)
261 ASSERT(taskq_member(console_taskq
, curthread
));
263 if (rconsvp
== NULL
|| panicstr
||
264 vn_rdwr(UIO_WRITE
, console_vnode
, cm
->cm_text
, strlen(cm
->cm_text
),
265 0, UIO_SYSSPACE
, FAPPEND
, (rlim64_t
)LOG_HIWAT
, kcred
, &res
) != 0) {
267 busy
= console_hold();
268 spl
= console_enter(busy
);
270 prom_printf("%s", cm
->cm_text
);
272 console_exit(busy
, spl
);
276 kmem_free(cm
, cm
->cm_size
);
280 console_vprintf(const char *fmt
, va_list adx
)
283 size_t len
= vsnprintf(NULL
, 0, fmt
, adx
);
286 if (console_taskq
!= NULL
&& rconsvp
!= NULL
&& panicstr
== NULL
&&
287 (cm
= kmem_alloc(sizeof (*cm
) + len
, KM_NOSLEEP
)) != NULL
) {
288 cm
->cm_size
= sizeof (*cm
) + len
;
289 (void) vsnprintf(cm
->cm_text
, len
+ 1, fmt
, adx
);
290 if (taskq_dispatch(console_taskq
, (task_func_t
*)console_putmsg
,
291 cm
, TQ_NOSLEEP
) != 0)
293 kmem_free(cm
, cm
->cm_size
);
296 busy
= console_hold();
297 spl
= console_enter(busy
);
299 prom_vprintf(fmt
, adx
);
301 console_exit(busy
, spl
);
307 console_printf(const char *fmt
, ...)
312 console_vprintf(fmt
, adx
);
317 * Avoid calling this function.
319 * Nothing in the kernel besides the wscons driver (wc) uses this
320 * function. It may hopefully one day be removed altogether.
321 * If a wayward module calls this they will pass through to PROM,
322 * get redirected into the kernel emulator as appropriate.
325 console_puts(const char *s
, size_t n
)
329 busy
= console_hold();
330 spl
= console_enter(busy
);
334 console_exit(busy
, spl
);
339 * Let this function just go straight through to the PROM, since
340 * we are called in early boot prior to the kernel terminal
341 * emulator being available, and prior to the PROM stdout redirect
347 int busy
= console_hold();
348 int spl
= console_enter(busy
);
354 console_exit(busy
, spl
);
359 * Read a string from the console device. We only permit synchronous
360 * conversation between the kernel and a console user early in boot prior to
361 * the initialization of rconsvp.
364 console_gets(char *s
, size_t len
)
367 char *q
= s
+ len
- 1;
370 ASSERT(rconsvp
== NULL
);
371 (void) console_hold();
374 switch (c
= (prom_getchar() & 0x7f)) {
389 case '#': /* historical backspace alias */
420 * Read a character from the console device. Synchronous conversation between
421 * the kernel and a console user is only permitted early in boot prior to the
422 * initialization of rconsvp.
429 ASSERT(rconsvp
== NULL
);