1 /* 2 * CDDL HEADER START 3 * 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. 7 * 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. 12 * 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] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 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> 34 #include <sys/note.h> 35 #include <sys/polled_io.h> 36 #include <sys/systm.h> 37 #include <sys/file.h> 38 #include <sys/conf.h> 39 #include <sys/kmem.h> 40 #include <sys/taskq.h> 41 #include <sys/log.h> 42 #include <sys/ddi.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> 48 49 #define MINLINES 10 50 #define MAXLINES 48 51 #define LOSCREENLINES 34 52 #define HISCREENLINES 48 53 54 #define MINCOLS 10 55 #define MAXCOLS 120 56 #define LOSCREENCOLS 80 57 #define HISCREENCOLS 120 58 59 vnode_t *console_vnode; 60 taskq_t *console_taskq; 61 62 /* 63 * The current set of polled I/O routines (if any) 64 */ 65 struct cons_polledio *cons_polledio; 66 67 /* 68 * Console I/O Routines 69 * 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. 80 * 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). 94 * 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. 102 */ 103 104 static krwlock_t console_lock; 105 static uint_t console_depth; 106 static int console_busy; 107 108 extern void pm_cfb_check_and_powerup(void); 109 extern void pm_cfb_rele(void); 110 111 static int 112 console_hold(void) 113 { 114 if (panicstr != NULL) 115 return (console_busy); /* assume exclusive access in panic */ 116 117 if (rw_owner(&console_lock) != curthread) 118 rw_enter(&console_lock, RW_WRITER); 119 120 if (console_depth++ != 0) 121 return (console_busy); /* lock is being entered recursively */ 122 123 pm_cfb_check_and_powerup(); 124 125 #ifdef _HAVE_TEM_FIRMWARE 126 if (consmode == CONS_FW && ncpus > 1 && fbvp != NULL) { 127 struct snode *csp = VTOS(VTOS(fbvp)->s_commonvp); 128 129 mutex_enter(&csp->s_lock); 130 console_busy = csp->s_mapcnt != 0; 131 132 if (csp->s_mapcnt == 0 && fbdip != NULL) { 133 mutex_enter(&DEVI(fbdip)->devi_lock); 134 console_busy = DEVI(fbdip)->devi_ref != 0; 135 } 136 } 137 #endif /* _HAVE_TEM_FIRMWARE */ 138 return (console_busy); 139 } 140 141 static void 142 console_rele(void) 143 { 144 if (panicstr != NULL) 145 return; /* do not modify lock states if we are panicking */ 146 147 ASSERT(RW_WRITE_HELD(&console_lock)); 148 ASSERT(console_depth != 0); 149 150 if (--console_depth != 0) 151 return; /* lock is being dropped recursively */ 152 153 #ifdef _HAVE_TEM_FIRMWARE 154 if (consmode == CONS_FW && ncpus > 1 && fbvp != NULL) { 155 struct snode *csp = VTOS(VTOS(fbvp)->s_commonvp); 156 157 ASSERT(MUTEX_HELD(&csp->s_lock)); 158 if (csp->s_mapcnt == 0 && fbdip != NULL) 159 mutex_exit(&DEVI(fbdip)->devi_lock); 160 161 mutex_exit(&csp->s_lock); 162 } 163 #endif /* _HAVE_TEM_FIRMWARE */ 164 pm_cfb_rele(); 165 console_busy = 0; 166 rw_exit(&console_lock); 167 } 168 169 static void 170 console_getprop(dev_t dev, dev_info_t *dip, char *name, ushort_t *sp) 171 { 172 uchar_t *data; 173 uint_t len; 174 uint_t i; 175 176 *sp = 0; 177 if (ddi_prop_lookup_byte_array(dev, dip, 0, name, &data, &len) == 178 DDI_PROP_SUCCESS) { 179 for (i = 0; i < len; i++) { 180 if (data[i] < '0' || data[i] > '9') 181 break; 182 *sp = *sp * 10 + data[i] - '0'; 183 } 184 ddi_prop_free(data); 185 } 186 } 187 188 /* 189 * Gets the number of rows and columns (in char's) and the 190 * width and height (in pixels) of the console. 191 */ 192 void 193 console_get_size(ushort_t *r, ushort_t *c, ushort_t *x, ushort_t *y) 194 { 195 int rel_needed = 0; 196 dev_info_t *dip; 197 dev_t dev; 198 199 /* 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. 204 */ 205 if (rwsconsvp == NULL || consmode == CONS_FW) { 206 dip = ddi_root_node(); 207 dev = DDI_DEV_T_ANY; 208 } else { 209 dev = rwsconsvp->v_rdev; /* layering is wc -> tem */ 210 dip = e_ddi_hold_devi_by_dev(dev, 0); 211 rel_needed = 1; 212 } 213 214 /* 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. 218 */ 219 if (dip == NULL) { 220 *r = LOSCREENLINES; 221 *c = LOSCREENCOLS; 222 *x = *y = 0; 223 return; 224 } 225 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); 230 231 if (*c < MINCOLS) 232 *c = LOSCREENCOLS; 233 else if (*c > MAXCOLS) 234 *c = HISCREENCOLS; 235 236 if (*r < MINLINES) 237 *r = LOSCREENLINES; 238 else if (*r > MAXLINES) 239 *r = HISCREENLINES; 240 241 if (rel_needed) 242 ddi_release_devi(dip); 243 } 244 245 typedef struct console_msg { 246 size_t cm_size; 247 char cm_text[1]; 248 } console_msg_t; 249 250 /* 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. 254 */ 255 static void 256 console_putmsg(console_msg_t *cm) 257 { 258 int busy, spl; 259 ssize_t res; 260 261 ASSERT(taskq_member(console_taskq, curthread)); 262 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) { 266 267 busy = console_hold(); 268 spl = console_enter(busy); 269 270 prom_printf("%s", cm->cm_text); 271 272 console_exit(busy, spl); 273 console_rele(); 274 } 275 276 kmem_free(cm, cm->cm_size); 277 } 278 279 void 280 console_vprintf(const char *fmt, va_list adx) 281 { 282 console_msg_t *cm; 283 size_t len = vsnprintf(NULL, 0, fmt, adx); 284 int busy, spl; 285 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) != TASKQID_INVALID) 292 return; 293 kmem_free(cm, cm->cm_size); 294 } 295 296 busy = console_hold(); 297 spl = console_enter(busy); 298 299 prom_vprintf(fmt, adx); 300 301 console_exit(busy, spl); 302 console_rele(); 303 } 304 305 /*PRINTFLIKE1*/ 306 void 307 console_printf(const char *fmt, ...) 308 { 309 va_list adx; 310 311 va_start(adx, fmt); 312 console_vprintf(fmt, adx); 313 va_end(adx); 314 } 315 316 /* 317 * Avoid calling this function. 318 * 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. 323 */ 324 void 325 console_puts(const char *s, size_t n) 326 { 327 int busy, spl; 328 329 busy = console_hold(); 330 spl = console_enter(busy); 331 332 prom_writestr(s, n); 333 334 console_exit(busy, spl); 335 console_rele(); 336 } 337 338 /* 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 342 * vector being set. 343 */ 344 static void 345 console_putc(int c) 346 { 347 int busy = console_hold(); 348 int spl = console_enter(busy); 349 350 if (c == '\n') 351 prom_putchar('\r'); 352 prom_putchar(c); 353 354 console_exit(busy, spl); 355 console_rele(); 356 } 357 358 /* 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. 362 */ 363 void 364 console_gets(char *s, size_t len) 365 { 366 char *p = s; 367 char *q = s + len - 1; 368 int c; 369 370 ASSERT(rconsvp == NULL); 371 (void) console_hold(); 372 373 for (;;) { 374 switch (c = (prom_getchar() & 0x7f)) { 375 case 0x7f: /* DEL */ 376 if (p == s) 377 break; 378 console_putc(c); 379 c = '\b'; 380 /*FALLTHRU*/ 381 382 case '\b': 383 if (p == s) 384 break; 385 console_putc('\b'); 386 console_putc(' '); 387 /*FALLTHRU*/ 388 389 case '#': /* historical backspace alias */ 390 console_putc(c); 391 if (p > s) 392 p--; 393 break; 394 395 case CTRL('u'): 396 console_putc(c); 397 console_putc('\n'); 398 p = s; 399 break; 400 401 case '\r': 402 case '\n': 403 console_putc('\n'); 404 goto done; 405 406 default: 407 if (p < q) { 408 console_putc(c); 409 *p++ = c; 410 } else 411 console_putc('\a'); 412 } 413 } 414 done: 415 console_rele(); 416 *p = '\0'; 417 } 418 419 /* 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. 423 */ 424 int 425 console_getc(void) 426 { 427 int c; 428 429 ASSERT(rconsvp == NULL); 430 c = prom_getchar(); 431 432 if (c == '\r') 433 c = '\n'; 434 435 console_putc(c); 436 return (c); 437 } 438