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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/varargs.h> 31 #include <sys/modctl.h> 32 #include <sys/cmn_err.h> 33 #include <sys/console.h> 34 #include <sys/consdev.h> 35 #include <sys/promif.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 48 #define MINLINES 10 49 #define MAXLINES 48 50 #define LOSCREENLINES 34 51 #define HISCREENLINES 48 52 53 #define MINCOLS 10 54 #define MAXCOLS 120 55 #define LOSCREENCOLS 80 56 #define HISCREENCOLS 120 57 58 vnode_t *console_vnode; 59 taskq_t *console_taskq; 60 vnode_t *ltemvp = NULL; 61 dev_t ltemdev; 62 63 /* 64 * The current set of polled I/O routines (if any) 65 */ 66 struct cons_polledio *cons_polledio; 67 68 /* 69 * Console I/O Routines 70 * 71 * In the event that kernel messages are generated with cmn_err(9F) or printf() 72 * early in boot, after a panic, in resource-constrained situations, or sent 73 * through /dev/console to the wscons driver, we may be called upon to render 74 * characters directly to the frame buffer using the underlying prom_*() 75 * routines. These in turn may attempt to use PROM services directly, or may 76 * use a kernel console emulator if one is available. Unfortunately, if PROM 77 * services are being used by the kernel on a multi-CPU system, these routines 78 * might be called while another CPU is simultaneously accessing a frame buffer 79 * memory mapping (perhaps through the X server). This situation may not be 80 * supported by the frame buffer hardware. 81 * 82 * To handle this situation, we implement a two-phase locking scheme which we 83 * use to protect accesses to the underlying prom_*() rendering routines. The 84 * common-code functions console_hold() and console_rele() are used to gain 85 * exclusive access to the console from within the kernel. We use a standard 86 * r/w lock in writer-mode only to implement the kernel lock. We use an r/w 87 * lock instead of a mutex here because character rendering is slow and hold 88 * times will be relatively long, and there is no point in adaptively spinning. 89 * These routines may be called recursively, in which case subsequent calls 90 * just increment the console_depth hold count. Once exclusive access is 91 * gained, we grab the frame buffer device node and block further mappings to 92 * it by holding the specfs node lock and the device node's lock. We then 93 * observe if any mappings are present by examining the specfs node's s_mapcnt 94 * (non-clone mmaps) and the devinfo node's devi_ref count (clone opens). 95 * 96 * Then, around each character rendering call, the routines console_enter() 97 * and console_exit() are used to inform the platform code that we are 98 * accessing the character rendering routines. These platform routines can 99 * then examine the "busy" flag returned by console_enter() and briefly stop 100 * the other CPUs so that they cannot access the frame buffer hardware while 101 * we are busy rendering characters. This mess can all be removed when the 102 * impossible dream of a unified kernel console emulator is someday realized. 103 */ 104 105 static krwlock_t console_lock; 106 static uint_t console_depth; 107 static int console_busy; 108 109 extern void pm_cfb_check_and_powerup(void); 110 extern void pm_cfb_rele(void); 111 112 static int 113 console_hold(void) 114 { 115 if (panicstr != NULL) 116 return (console_busy); /* assume exclusive access in panic */ 117 118 if (rw_owner(&console_lock) != curthread) 119 rw_enter(&console_lock, RW_WRITER); 120 121 if (console_depth++ != 0) 122 return (console_busy); /* lock is being entered recursively */ 123 124 pm_cfb_check_and_powerup(); 125 126 #ifdef _CONSOLE_OUTPUT_VIA_FIRMWARE 127 if (ncpus > 1 && fbvp != NULL) { 128 struct snode *csp = VTOS(VTOS(fbvp)->s_commonvp); 129 130 mutex_enter(&csp->s_lock); 131 console_busy = csp->s_mapcnt != 0; 132 133 if (csp->s_mapcnt == 0 && fbdip != NULL) { 134 mutex_enter(&DEVI(fbdip)->devi_lock); 135 console_busy = DEVI(fbdip)->devi_ref != 0; 136 } 137 } 138 #endif 139 return (console_busy); 140 } 141 142 static void 143 console_rele(void) 144 { 145 if (panicstr != NULL) 146 return; /* do not modify lock states if we are panicking */ 147 148 ASSERT(RW_WRITE_HELD(&console_lock)); 149 ASSERT(console_depth != 0); 150 151 if (--console_depth != 0) 152 return; /* lock is being dropped recursively */ 153 154 #ifdef _CONSOLE_OUTPUT_VIA_FIRMWARE 155 if (ncpus > 1 && fbvp != NULL) { 156 struct snode *csp = VTOS(VTOS(fbvp)->s_commonvp); 157 158 ASSERT(MUTEX_HELD(&csp->s_lock)); 159 if (csp->s_mapcnt == 0 && fbdip != NULL) 160 mutex_exit(&DEVI(fbdip)->devi_lock); 161 162 mutex_exit(&csp->s_lock); 163 } 164 #endif 165 pm_cfb_rele(); 166 console_busy = 0; 167 rw_exit(&console_lock); 168 } 169 170 static void 171 console_getprop(dev_t dev, dev_info_t *dip, char *name, ushort_t *sp) 172 { 173 uchar_t *data; 174 uint_t len; 175 uint_t i; 176 177 *sp = 0; 178 if (ddi_prop_lookup_byte_array(dev, dip, 0, name, &data, &len) == 179 DDI_PROP_SUCCESS) { 180 for (i = 0; i < len; i++) { 181 if (data[i] < '0' || data[i] > '9') 182 break; 183 *sp = *sp * 10 + data[i] - '0'; 184 } 185 ddi_prop_free(data); 186 } 187 } 188 189 /* 190 * Gets the number of rows and columns (in char's) and the 191 * width and height (in pixels) of the console. 192 */ 193 void 194 console_get_size(ushort_t *r, ushort_t *c, ushort_t *x, ushort_t *y) 195 { 196 int rel_needed = 0; 197 dev_info_t *dip; 198 dev_t dev; 199 200 /* 201 * If we have loaded the console IO stuff, then ask for the screen 202 * size properties from the layered terminal emulator. Else ask for 203 * them from the root node, which will eventually fall through to the 204 * options node and get them from the prom. 205 */ 206 if (ltemvp == NULL) { 207 dip = ddi_root_node(); 208 dev = DDI_DEV_T_ANY; 209 } else { 210 dip = e_ddi_hold_devi_by_dev(ltemdev, 0); 211 dev = ltemdev; 212 rel_needed = 1; 213 } 214 215 /* 216 * If we have not initialized a console yet and don't have a root 217 * node (ie. we have not initialized the DDI yet) return our default 218 * size for the screen. 219 */ 220 if (dip == NULL) { 221 *r = LOSCREENLINES; 222 *c = LOSCREENCOLS; 223 *x = *y = 0; 224 return; 225 } 226 227 console_getprop(dev, dip, "screen-#columns", c); 228 console_getprop(dev, dip, "screen-#rows", r); 229 console_getprop(dev, dip, "screen-width", x); 230 console_getprop(dev, dip, "screen-height", y); 231 232 if (*c < MINCOLS) 233 *c = LOSCREENCOLS; 234 else if (*c > MAXCOLS) 235 *c = HISCREENCOLS; 236 237 if (*r < MINLINES) 238 *r = LOSCREENLINES; 239 else if (*r > MAXLINES) 240 *r = HISCREENLINES; 241 242 if (rel_needed) 243 ddi_release_devi(dip); 244 } 245 246 typedef struct console_msg { 247 size_t cm_size; 248 char cm_text[1]; 249 } console_msg_t; 250 251 static void 252 console_putmsg(console_msg_t *cm) 253 { 254 int busy, spl; 255 ssize_t res; 256 257 ASSERT(taskq_member(console_taskq, curthread)); 258 259 if (rconsvp == NULL || panicstr || 260 vn_rdwr(UIO_WRITE, console_vnode, cm->cm_text, strlen(cm->cm_text), 261 0, UIO_SYSSPACE, FAPPEND, (rlim64_t)LOG_HIWAT, kcred, &res) != 0) { 262 263 busy = console_hold(); 264 spl = console_enter(busy); 265 266 prom_printf("%s", cm->cm_text); 267 268 console_exit(busy, spl); 269 console_rele(); 270 } 271 272 kmem_free(cm, cm->cm_size); 273 } 274 275 void 276 console_vprintf(const char *fmt, va_list adx) 277 { 278 console_msg_t *cm; 279 size_t len = vsnprintf(NULL, 0, fmt, adx); 280 int busy, spl; 281 282 if (console_taskq != NULL && rconsvp != NULL && panicstr == NULL && 283 (cm = kmem_alloc(sizeof (*cm) + len, KM_NOSLEEP)) != NULL) { 284 cm->cm_size = sizeof (*cm) + len; 285 (void) vsnprintf(cm->cm_text, len + 1, fmt, adx); 286 if (taskq_dispatch(console_taskq, (task_func_t *)console_putmsg, 287 cm, TQ_NOSLEEP) != 0) 288 return; 289 kmem_free(cm, cm->cm_size); 290 } 291 292 busy = console_hold(); 293 spl = console_enter(busy); 294 295 prom_vprintf(fmt, adx); 296 297 console_exit(busy, spl); 298 console_rele(); 299 } 300 301 /*PRINTFLIKE1*/ 302 void 303 console_printf(const char *fmt, ...) 304 { 305 va_list adx; 306 307 va_start(adx, fmt); 308 console_vprintf(fmt, adx); 309 va_end(adx); 310 } 311 312 /* 313 * Write the specified number of bytes from a string to the console device. 314 */ 315 void 316 console_puts(const char *s, size_t n) 317 { 318 int busy, spl; 319 320 busy = console_hold(); 321 spl = console_enter(busy); 322 323 prom_writestr(s, n); 324 325 console_exit(busy, spl); 326 console_rele(); 327 } 328 329 void 330 console_putc(int c) 331 { 332 int busy = console_hold(); 333 int spl = console_enter(busy); 334 335 if (c == '\n') 336 prom_putchar('\r'); 337 prom_putchar(c); 338 339 console_exit(busy, spl); 340 console_rele(); 341 } 342 343 /* 344 * Read a string from the console device. We only permit synchronous 345 * conversation between the kernel and a console user early in boot prior to 346 * the initialization of rconsvp. 347 */ 348 void 349 console_gets(char *s, size_t len) 350 { 351 char *p = s; 352 char *q = s + len - 1; 353 int c; 354 355 ASSERT(rconsvp == NULL); 356 (void) console_hold(); 357 358 for (;;) { 359 switch (c = (prom_getchar() & 0x7f)) { 360 case 0x7f: /* DEL */ 361 if (p == s) 362 break; 363 console_putc(c); 364 c = '\b'; 365 /*FALLTHRU*/ 366 367 case '\b': 368 if (p == s) 369 break; 370 console_putc('\b'); 371 console_putc(' '); 372 /*FALLTHRU*/ 373 374 case '#': /* historical backspace alias */ 375 console_putc(c); 376 if (p > s) 377 p--; 378 break; 379 380 case CTRL('u'): 381 console_putc(c); 382 console_putc('\n'); 383 p = s; 384 break; 385 386 case '\r': 387 case '\n': 388 console_putc('\n'); 389 goto done; 390 391 default: 392 if (p < q) { 393 console_putc(c); 394 *p++ = c; 395 } else 396 console_putc('\a'); 397 } 398 } 399 done: 400 console_rele(); 401 *p = '\0'; 402 } 403 404 /* 405 * Read a character from the console device. Synchronous conversation between 406 * the kernel and a console user is only permitted early in boot prior to the 407 * initialization of rconsvp. 408 */ 409 int 410 console_getc(void) 411 { 412 int c; 413 414 ASSERT(rconsvp == NULL); 415 c = prom_getchar(); 416 417 if (c == '\r') 418 c = '\n'; 419 420 console_putc(c); 421 return (c); 422 } 423