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 /* 28 * ANSI terminal emulator module; parse ANSI X3.64 escape sequences and 29 * the like. 30 * 31 * How Virtual Terminal Emulator Works: 32 * 33 * Every virtual terminal is associated with a tem_vt_state structure 34 * and maintains a virtual screen buffer in tvs_screen_buf, which contains 35 * all the characters which should be shown on the physical screen when 36 * the terminal is activated. There are also two other buffers, tvs_fg_buf 37 * and tvs_bg_buf, which track the foreground and background colors of the 38 * on screen characters 39 * 40 * Data written to a virtual terminal is composed of characters which 41 * should be displayed on the screen when this virtual terminal is 42 * activated, fg/bg colors of these characters, and other control 43 * information (escape sequence, etc). 44 * 45 * When data is passed to a virtual terminal it first is parsed for 46 * control information by tem_safe_parse(). Subsequently the character 47 * and color data are written to tvs_screen_buf, tvs_fg_buf, and 48 * tvs_bg_buf. They are saved in these buffers in order to refresh 49 * the screen when this terminal is activated. If the terminal is 50 * currently active, the data (characters and colors) are also written 51 * to the physical screen by invoking a callback function, 52 * tem_safe_text_callbacks() or tem_safe_pix_callbacks(). 53 * 54 * When rendering data to the framebuffer, if the framebuffer is in 55 * VIS_PIXEL mode, the character data will first be converted to pixel 56 * data using tem_safe_pix_bit2pix(), and then the pixels get displayed 57 * on the physical screen. We only store the character and color data in 58 * tem_vt_state since the bit2pix conversion only happens when actually 59 * rendering to the physical framebuffer. 60 */ 61 62 63 #include <sys/types.h> 64 #include <sys/file.h> 65 #include <sys/conf.h> 66 #include <sys/errno.h> 67 #include <sys/open.h> 68 #include <sys/cred.h> 69 #include <sys/kmem.h> 70 #include <sys/ascii.h> 71 #include <sys/consdev.h> 72 #include <sys/font.h> 73 #include <sys/fbio.h> 74 #include <sys/conf.h> 75 #include <sys/modctl.h> 76 #include <sys/strsubr.h> 77 #include <sys/stat.h> 78 #include <sys/visual_io.h> 79 #include <sys/mutex.h> 80 #include <sys/param.h> 81 #include <sys/debug.h> 82 #include <sys/cmn_err.h> 83 #include <sys/console.h> 84 #include <sys/ddi.h> 85 #include <sys/sunddi.h> 86 #include <sys/sunldi.h> 87 #include <sys/tem_impl.h> 88 #ifdef _HAVE_TEM_FIRMWARE 89 #include <sys/promif.h> 90 #endif /* _HAVE_TEM_FIRMWARE */ 91 #include <sys/consplat.h> 92 #include <sys/kd.h> 93 #include <sys/sysmacros.h> 94 #include <sys/note.h> 95 #include <sys/t_lock.h> 96 97 /* Terminal emulator internal helper functions */ 98 static void tems_setup_terminal(struct vis_devinit *, size_t, size_t); 99 static void tems_modechange_callback(struct vis_modechg_arg *, 100 struct vis_devinit *); 101 102 static void tems_reset_colormap(cred_t *, enum called_from); 103 104 static void tem_free_buf(struct tem_vt_state *); 105 static void tem_internal_init(struct tem_vt_state *, cred_t *, boolean_t, 106 boolean_t); 107 static void tems_get_initial_color(tem_color_t *pcolor); 108 109 /* 110 * Globals 111 */ 112 static ldi_ident_t term_li = NULL; 113 tem_state_t tems; /* common term info */ 114 _NOTE(MUTEX_PROTECTS_DATA(tems.ts_lock, tems)) 115 116 extern struct mod_ops mod_miscops; 117 118 static struct modlmisc modlmisc = { 119 &mod_miscops, /* modops */ 120 "ANSI Terminal Emulator", /* name */ 121 }; 122 123 static struct modlinkage modlinkage = { 124 MODREV_1, { (void *)&modlmisc, NULL } 125 }; 126 127 int 128 _init(void) 129 { 130 int ret; 131 ret = mod_install(&modlinkage); 132 if (ret != 0) 133 return (ret); 134 ret = ldi_ident_from_mod(&modlinkage, &term_li); 135 if (ret != 0) { 136 (void) mod_remove(&modlinkage); 137 return (ret); 138 } 139 140 mutex_init(&tems.ts_lock, (char *)NULL, MUTEX_DRIVER, NULL); 141 list_create(&tems.ts_list, sizeof (struct tem_vt_state), 142 offsetof(struct tem_vt_state, tvs_list_node)); 143 tems.ts_active = NULL; 144 145 return (0); 146 } 147 148 int 149 _fini() 150 { 151 int ret; 152 153 ret = mod_remove(&modlinkage); 154 if (ret == 0) { 155 ldi_ident_release(term_li); 156 term_li = NULL; 157 } 158 return (ret); 159 } 160 161 int 162 _info(struct modinfo *modinfop) 163 { 164 return (mod_info(&modlinkage, modinfop)); 165 } 166 167 static void 168 tem_add(struct tem_vt_state *tem) 169 { 170 ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)); 171 172 list_insert_head(&tems.ts_list, tem); 173 } 174 175 static void 176 tem_rm(struct tem_vt_state *tem) 177 { 178 ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)); 179 180 list_remove(&tems.ts_list, tem); 181 } 182 183 /* 184 * This is the main entry point to the module. It handles output requests 185 * during normal system operation, when (e.g.) mutexes are available. 186 */ 187 void 188 tem_write(tem_vt_state_t tem_arg, uchar_t *buf, ssize_t len, cred_t *credp) 189 { 190 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg; 191 192 mutex_enter(&tems.ts_lock); 193 mutex_enter(&tem->tvs_lock); 194 195 if (!tem->tvs_initialized) { 196 mutex_exit(&tem->tvs_lock); 197 mutex_exit(&tems.ts_lock); 198 return; 199 } 200 201 tem_safe_check_first_time(tem, credp, CALLED_FROM_NORMAL); 202 tem_safe_terminal_emulate(tem, buf, len, credp, CALLED_FROM_NORMAL); 203 204 mutex_exit(&tem->tvs_lock); 205 mutex_exit(&tems.ts_lock); 206 } 207 208 static void 209 tem_internal_init(struct tem_vt_state *ptem, cred_t *credp, 210 boolean_t init_color, boolean_t clear_screen) 211 { 212 unsigned i, j, width, height; 213 text_attr_t attr; 214 text_color_t fg; 215 text_color_t bg; 216 217 ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&ptem->tvs_lock)); 218 219 if (tems.ts_display_mode == VIS_PIXEL) { 220 ptem->tvs_pix_data_size = tems.ts_pix_data_size; 221 ptem->tvs_pix_data = 222 kmem_alloc(ptem->tvs_pix_data_size, KM_SLEEP); 223 } 224 225 ptem->tvs_stateflags = TVS_AUTOWRAP; 226 227 ptem->tvs_outbuf_size = tems.ts_c_dimension.width * 228 sizeof (*ptem->tvs_outbuf); 229 ptem->tvs_outbuf = kmem_alloc(ptem->tvs_outbuf_size, KM_SLEEP); 230 231 width = tems.ts_c_dimension.width; 232 height = tems.ts_c_dimension.height; 233 ptem->tvs_screen_history_size = height; 234 235 ptem->tvs_screen_buf_size = width * ptem->tvs_screen_history_size * 236 sizeof (*ptem->tvs_screen_buf); 237 ptem->tvs_screen_buf = kmem_alloc(ptem->tvs_screen_buf_size, KM_SLEEP); 238 ptem->tvs_screen_rows = kmem_alloc(ptem->tvs_screen_history_size * 239 sizeof (term_char_t *), KM_SLEEP); 240 241 ptem->tvs_maxtab = width / 8; 242 ptem->tvs_tabs = kmem_alloc(ptem->tvs_maxtab * sizeof (*ptem->tvs_tabs), 243 KM_SLEEP); 244 245 tem_safe_reset_display(ptem, credp, CALLED_FROM_NORMAL, 246 clear_screen, init_color); 247 248 ptem->tvs_utf8_left = 0; 249 ptem->tvs_utf8_partial = 0; 250 251 /* Get default attributes and fill up the screen buffer. */ 252 tem_safe_get_attr(ptem, &fg, &bg, &attr, TEM_ATTR_SCREEN_REVERSE); 253 for (i = 0; i < ptem->tvs_screen_history_size; i++) { 254 ptem->tvs_screen_rows[i] = &ptem->tvs_screen_buf[i * width]; 255 256 for (j = 0; j < width; j++) { 257 ptem->tvs_screen_rows[i][j].tc_fg_color = fg; 258 ptem->tvs_screen_rows[i][j].tc_bg_color = bg; 259 ptem->tvs_screen_rows[i][j].tc_char = 260 TEM_ATTR(attr) | ' '; 261 } 262 } 263 264 ptem->tvs_initialized = B_TRUE; 265 } 266 267 boolean_t 268 tem_initialized(tem_vt_state_t tem_arg) 269 { 270 struct tem_vt_state *ptem = (struct tem_vt_state *)tem_arg; 271 boolean_t ret; 272 273 mutex_enter(&ptem->tvs_lock); 274 ret = ptem->tvs_initialized; 275 mutex_exit(&ptem->tvs_lock); 276 277 return (ret); 278 } 279 280 tem_vt_state_t 281 tem_init(cred_t *credp, queue_t *rq) 282 { 283 struct tem_vt_state *ptem; 284 285 ptem = kmem_zalloc(sizeof (struct tem_vt_state), KM_SLEEP); 286 mutex_init(&ptem->tvs_lock, (char *)NULL, MUTEX_DRIVER, NULL); 287 288 mutex_enter(&tems.ts_lock); 289 mutex_enter(&ptem->tvs_lock); 290 291 ptem->tvs_isactive = B_FALSE; 292 ptem->tvs_fbmode = KD_TEXT; 293 ptem->tvs_queue = rq; 294 295 /* 296 * A tem is regarded as initialized only after tem_internal_init(), 297 * will be set at the end of tem_internal_init(). 298 */ 299 ptem->tvs_initialized = B_FALSE; 300 301 302 if (!tems.ts_initialized) { 303 /* 304 * Only happens during early console configuration. 305 */ 306 tem_add(ptem); 307 mutex_exit(&ptem->tvs_lock); 308 mutex_exit(&tems.ts_lock); 309 return ((tem_vt_state_t)ptem); 310 } 311 312 tem_internal_init(ptem, credp, B_TRUE, B_FALSE); 313 tem_add(ptem); 314 mutex_exit(&ptem->tvs_lock); 315 mutex_exit(&tems.ts_lock); 316 317 return ((tem_vt_state_t)ptem); 318 } 319 320 /* 321 * re-init the tem after video mode has changed and tems_info has 322 * been re-inited. The lock is already held. 323 */ 324 static void 325 tem_reinit(struct tem_vt_state *tem, boolean_t reset_display) 326 { 327 ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)); 328 329 tem_free_buf(tem); /* only free virtual buffers */ 330 331 /* reserve color */ 332 tem_internal_init(tem, kcred, B_FALSE, reset_display); 333 } 334 335 static void 336 tem_free_buf(struct tem_vt_state *tem) 337 { 338 ASSERT(tem != NULL && MUTEX_HELD(&tem->tvs_lock)); 339 340 if (tem->tvs_outbuf != NULL) 341 kmem_free(tem->tvs_outbuf, tem->tvs_outbuf_size); 342 if (tem->tvs_pix_data != NULL) 343 kmem_free(tem->tvs_pix_data, tem->tvs_pix_data_size); 344 if (tem->tvs_screen_buf != NULL) 345 kmem_free(tem->tvs_screen_buf, tem->tvs_screen_buf_size); 346 if (tem->tvs_screen_rows != NULL) { 347 kmem_free(tem->tvs_screen_rows, tem->tvs_screen_history_size * 348 sizeof (term_char_t *)); 349 } 350 if (tem->tvs_tabs != NULL) { 351 kmem_free(tem->tvs_tabs, tem->tvs_maxtab * 352 sizeof (*tem->tvs_tabs)); 353 } 354 } 355 356 void 357 tem_destroy(tem_vt_state_t tem_arg, cred_t *credp) 358 { 359 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg; 360 361 mutex_enter(&tems.ts_lock); 362 mutex_enter(&tem->tvs_lock); 363 364 if (tem->tvs_isactive && tem->tvs_fbmode == KD_TEXT) 365 tem_safe_blank_screen(tem, credp, CALLED_FROM_NORMAL); 366 367 tem_free_buf(tem); 368 tem_rm(tem); 369 370 if (tems.ts_active == tem) 371 tems.ts_active = NULL; 372 373 mutex_exit(&tem->tvs_lock); 374 mutex_exit(&tems.ts_lock); 375 376 kmem_free(tem, sizeof (struct tem_vt_state)); 377 } 378 379 static int 380 tems_failed(cred_t *credp, boolean_t finish_ioctl) 381 { 382 int lyr_rval; 383 384 ASSERT(MUTEX_HELD(&tems.ts_lock)); 385 386 if (finish_ioctl) 387 (void) ldi_ioctl(tems.ts_hdl, VIS_DEVFINI, 0, 388 FWRITE | FKIOCTL, credp, &lyr_rval); 389 390 (void) ldi_close(tems.ts_hdl, 0, credp); 391 tems.ts_hdl = NULL; 392 return (ENXIO); 393 } 394 395 /* 396 * only called once during boot 397 */ 398 int 399 tem_info_init(char *pathname, cred_t *credp) 400 { 401 int lyr_rval, ret; 402 struct vis_devinit temargs; 403 char *pathbuf; 404 size_t height = 0; 405 size_t width = 0; 406 struct tem_vt_state *p; 407 408 mutex_enter(&tems.ts_lock); 409 410 if (tems.ts_initialized) { 411 mutex_exit(&tems.ts_lock); 412 return (0); 413 } 414 415 /* 416 * Open the layered device using the devfs physical device name 417 * after adding the /devices prefix. 418 */ 419 pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 420 (void) strcpy(pathbuf, "/devices"); 421 if (i_ddi_prompath_to_devfspath(pathname, 422 pathbuf + strlen("/devices")) != DDI_SUCCESS) { 423 cmn_err(CE_WARN, "terminal-emulator: path conversion error"); 424 kmem_free(pathbuf, MAXPATHLEN); 425 426 mutex_exit(&tems.ts_lock); 427 return (ENXIO); 428 } 429 if (ldi_open_by_name(pathbuf, FWRITE, credp, 430 &tems.ts_hdl, term_li) != 0) { 431 cmn_err(CE_WARN, "terminal-emulator: device path open error"); 432 kmem_free(pathbuf, MAXPATHLEN); 433 434 mutex_exit(&tems.ts_lock); 435 return (ENXIO); 436 } 437 kmem_free(pathbuf, MAXPATHLEN); 438 439 temargs.modechg_cb = (vis_modechg_cb_t)tems_modechange_callback; 440 temargs.modechg_arg = NULL; 441 442 /* 443 * Initialize the console and get the device parameters 444 */ 445 if (ldi_ioctl(tems.ts_hdl, VIS_DEVINIT, 446 (intptr_t)&temargs, FWRITE|FKIOCTL, credp, &lyr_rval) != 0) { 447 cmn_err(CE_WARN, "terminal emulator: Compatible fb not found"); 448 ret = tems_failed(credp, B_FALSE); 449 mutex_exit(&tems.ts_lock); 450 return (ret); 451 } 452 453 /* Make sure the fb driver and terminal emulator versions match */ 454 if (temargs.version != VIS_CONS_REV) { 455 cmn_err(CE_WARN, 456 "terminal emulator: VIS_CONS_REV %d (see sys/visual_io.h) " 457 "of console fb driver not supported", temargs.version); 458 ret = tems_failed(credp, B_TRUE); 459 mutex_exit(&tems.ts_lock); 460 return (ret); 461 } 462 463 if ((tems.ts_fb_polledio = temargs.polledio) == NULL) { 464 cmn_err(CE_WARN, "terminal emulator: fb doesn't support polled " 465 "I/O"); 466 ret = tems_failed(credp, B_TRUE); 467 mutex_exit(&tems.ts_lock); 468 return (ret); 469 } 470 471 /* other sanity checks */ 472 if (!((temargs.depth == 4) || (temargs.depth == 8) || 473 (temargs.depth == 15) || (temargs.depth == 16) || 474 (temargs.depth == 24) || (temargs.depth == 32))) { 475 cmn_err(CE_WARN, "terminal emulator: unsupported depth"); 476 ret = tems_failed(credp, B_TRUE); 477 mutex_exit(&tems.ts_lock); 478 return (ret); 479 } 480 481 if ((temargs.mode != VIS_TEXT) && (temargs.mode != VIS_PIXEL)) { 482 cmn_err(CE_WARN, "terminal emulator: unsupported mode"); 483 ret = tems_failed(credp, B_TRUE); 484 mutex_exit(&tems.ts_lock); 485 return (ret); 486 } 487 488 if ((temargs.mode == VIS_PIXEL) && plat_stdout_is_framebuffer()) 489 plat_tem_get_prom_size(&height, &width); 490 491 /* 492 * Initialize the common terminal emulator info 493 */ 494 tems_setup_terminal(&temargs, height, width); 495 496 tems_reset_colormap(credp, CALLED_FROM_NORMAL); 497 tems_get_initial_color(&tems.ts_init_color); 498 499 tems.ts_initialized = 1; /* initialization flag */ 500 501 for (p = list_head(&tems.ts_list); p != NULL; 502 p = list_next(&tems.ts_list, p)) { 503 mutex_enter(&p->tvs_lock); 504 tem_internal_init(p, credp, B_TRUE, B_FALSE); 505 tem_align(p, credp, CALLED_FROM_NORMAL); 506 mutex_exit(&p->tvs_lock); 507 } 508 509 mutex_exit(&tems.ts_lock); 510 return (0); 511 } 512 513 #define TEMS_DEPTH_DIFF 0x01 514 #define TEMS_DIMENSION_DIFF 0x02 515 516 static uchar_t 517 tems_check_videomode(struct vis_devinit *tp) 518 { 519 uchar_t result = 0; 520 521 if (tems.ts_pdepth != tp->depth) 522 result |= TEMS_DEPTH_DIFF; 523 524 if (tp->mode == VIS_TEXT) { 525 if (tems.ts_c_dimension.width != tp->width || 526 tems.ts_c_dimension.height != tp->height) 527 result |= TEMS_DIMENSION_DIFF; 528 } else { 529 if (tems.ts_p_dimension.width != tp->width || 530 tems.ts_p_dimension.height != tp->height) 531 result |= TEMS_DIMENSION_DIFF; 532 } 533 534 return (result); 535 } 536 537 static void 538 tems_setup_font(screen_size_t height, screen_size_t width) 539 { 540 bitmap_data_t *font_data; 541 int i; 542 543 /* 544 * set_font() will select an appropriate sized font for 545 * the number of rows and columns selected. If we don't 546 * have a font that will fit, then it will use the 547 * default builtin font and adjust the rows and columns 548 * to fit on the screen. 549 */ 550 font_data = set_font(&tems.ts_c_dimension.height, 551 &tems.ts_c_dimension.width, height, width); 552 553 /* 554 * To use loaded font, we assign the loaded font data to tems.ts_font. 555 * In case of next load, the previously loaded data is freed 556 * when loading the new font. 557 */ 558 for (i = 0; i < VFNT_MAPS; i++) { 559 tems.ts_font.vf_map[i] = 560 font_data->font->vf_map[i]; 561 tems.ts_font.vf_map_count[i] = 562 font_data->font->vf_map_count[i]; 563 } 564 565 tems.ts_font.vf_bytes = font_data->font->vf_bytes; 566 tems.ts_font.vf_width = font_data->font->vf_width; 567 tems.ts_font.vf_height = font_data->font->vf_height; 568 } 569 570 static void 571 tems_setup_terminal(struct vis_devinit *tp, size_t height, size_t width) 572 { 573 int old_blank_buf_size = tems.ts_c_dimension.width * 574 sizeof (*tems.ts_blank_line); 575 576 ASSERT(MUTEX_HELD(&tems.ts_lock)); 577 578 tems.ts_pdepth = tp->depth; 579 tems.ts_linebytes = tp->linebytes; 580 tems.ts_display_mode = tp->mode; 581 tems.ts_color_map = tp->color_map; 582 583 switch (tp->mode) { 584 case VIS_TEXT: 585 tems.ts_p_dimension.width = 0; 586 tems.ts_p_dimension.height = 0; 587 tems.ts_c_dimension.width = tp->width; 588 tems.ts_c_dimension.height = tp->height; 589 tems.ts_callbacks = &tem_safe_text_callbacks; 590 591 tems_setup_font(16 * tp->height + BORDER_PIXELS, 592 8 * tp->width + BORDER_PIXELS); 593 594 break; 595 596 case VIS_PIXEL: 597 /* 598 * First check to see if the user has specified a screen size. 599 * If so, use those values. Else use 34x80 as the default. 600 */ 601 if (width == 0) { 602 width = TEM_DEFAULT_COLS; 603 height = TEM_DEFAULT_ROWS; 604 } 605 tems.ts_c_dimension.height = (screen_size_t)height; 606 tems.ts_c_dimension.width = (screen_size_t)width; 607 tems.ts_p_dimension.height = tp->height; 608 tems.ts_p_dimension.width = tp->width; 609 tems.ts_callbacks = &tem_safe_pix_callbacks; 610 611 tems_setup_font(tp->height, tp->width); 612 613 tems.ts_p_offset.y = (tems.ts_p_dimension.height - 614 (tems.ts_c_dimension.height * tems.ts_font.vf_height)) / 2; 615 tems.ts_p_offset.x = (tems.ts_p_dimension.width - 616 (tems.ts_c_dimension.width * tems.ts_font.vf_width)) / 2; 617 618 tems.ts_pix_data_size = 619 tems.ts_font.vf_width * tems.ts_font.vf_height; 620 tems.ts_pix_data_size *= 4; 621 tems.ts_pdepth = tp->depth; 622 623 break; 624 } 625 626 /* Now virtual cls also uses the blank_line buffer */ 627 if (tems.ts_blank_line) 628 kmem_free(tems.ts_blank_line, old_blank_buf_size); 629 630 tems.ts_blank_line = kmem_alloc(tems.ts_c_dimension.width * 631 sizeof (*tems.ts_blank_line), KM_SLEEP); 632 } 633 634 /* 635 * This is a callback function that we register with the frame 636 * buffer driver layered underneath. It gets invoked from 637 * the underlying frame buffer driver to reconfigure the terminal 638 * emulator to a new screen size and depth in conjunction with 639 * framebuffer videomode changes. 640 * Here we keep the foreground/background color and attributes, 641 * which may be different with the initial settings, so that 642 * the color won't change while the framebuffer videomode changes. 643 * And we also reset the kernel terminal emulator and clear the 644 * whole screen. 645 */ 646 /* ARGSUSED */ 647 void 648 tems_modechange_callback(struct vis_modechg_arg *arg, 649 struct vis_devinit *devinit) 650 { 651 uchar_t diff; 652 struct tem_vt_state *p; 653 tem_modechg_cb_t cb; 654 tem_modechg_cb_arg_t cb_arg; 655 656 ASSERT(!(list_is_empty(&tems.ts_list))); 657 658 mutex_enter(&tems.ts_lock); 659 660 /* 661 * currently only for pixel mode 662 */ 663 diff = tems_check_videomode(devinit); 664 if (diff == 0) { 665 mutex_exit(&tems.ts_lock); 666 return; 667 } 668 669 diff = diff & TEMS_DIMENSION_DIFF; 670 671 if (diff == 0) { 672 /* 673 * Only need to reinit the active tem. 674 */ 675 struct tem_vt_state *active = tems.ts_active; 676 tems.ts_pdepth = devinit->depth; 677 678 mutex_enter(&active->tvs_lock); 679 ASSERT(active->tvs_isactive); 680 tem_reinit(active, B_TRUE); 681 mutex_exit(&active->tvs_lock); 682 683 mutex_exit(&tems.ts_lock); 684 return; 685 } 686 687 tems_setup_terminal(devinit, tems.ts_c_dimension.height, 688 tems.ts_c_dimension.width); 689 690 for (p = list_head(&tems.ts_list); p != NULL; 691 p = list_next(&tems.ts_list, p)) { 692 mutex_enter(&p->tvs_lock); 693 tem_reinit(p, p->tvs_isactive); 694 mutex_exit(&p->tvs_lock); 695 } 696 697 698 if (tems.ts_modechg_cb == NULL) { 699 mutex_exit(&tems.ts_lock); 700 return; 701 } 702 703 cb = tems.ts_modechg_cb; 704 cb_arg = tems.ts_modechg_arg; 705 706 /* 707 * Release the lock while doing callback. 708 */ 709 mutex_exit(&tems.ts_lock); 710 cb(cb_arg); 711 } 712 713 /* 714 * This function is used to clear entire screen via the underlying framebuffer 715 * driver. 716 */ 717 int 718 tems_cls_layered(struct vis_consclear *pda, 719 cred_t *credp) 720 { 721 int rval; 722 723 (void) ldi_ioctl(tems.ts_hdl, VIS_CONSCLEAR, 724 (intptr_t)pda, FKIOCTL, credp, &rval); 725 return (rval); 726 } 727 728 /* 729 * This function is used to display a rectangular blit of data 730 * of a given size and location via the underlying framebuffer driver. 731 * The blit can be as small as a pixel or as large as the screen. 732 */ 733 void 734 tems_display_layered(struct vis_consdisplay *pda, 735 cred_t *credp) 736 { 737 int rval; 738 739 (void) ldi_ioctl(tems.ts_hdl, VIS_CONSDISPLAY, 740 (intptr_t)pda, FKIOCTL, credp, &rval); 741 } 742 743 /* 744 * This function is used to invoke a block copy operation in the 745 * underlying framebuffer driver. Rectangle copies are how scrolling 746 * is implemented, as well as horizontal text shifting escape seqs. 747 * such as from vi when deleting characters and words. 748 */ 749 void 750 tems_copy_layered(struct vis_conscopy *pma, 751 cred_t *credp) 752 { 753 int rval; 754 755 (void) ldi_ioctl(tems.ts_hdl, VIS_CONSCOPY, 756 (intptr_t)pma, FKIOCTL, credp, &rval); 757 } 758 759 /* 760 * This function is used to show or hide a rectangluar monochrom 761 * pixel inverting, text block cursor via the underlying framebuffer. 762 */ 763 void 764 tems_cursor_layered(struct vis_conscursor *pca, 765 cred_t *credp) 766 { 767 int rval; 768 769 (void) ldi_ioctl(tems.ts_hdl, VIS_CONSCURSOR, 770 (intptr_t)pca, FKIOCTL, credp, &rval); 771 } 772 773 static void 774 tem_kdsetmode(int mode, cred_t *credp) 775 { 776 int rval; 777 778 (void) ldi_ioctl(tems.ts_hdl, KDSETMODE, 779 (intptr_t)mode, FKIOCTL, credp, &rval); 780 781 } 782 783 static void 784 tems_reset_colormap(cred_t *credp, enum called_from called_from) 785 { 786 struct vis_cmap cm; 787 int rval; 788 789 if (called_from == CALLED_FROM_STANDALONE) 790 return; 791 792 switch (tems.ts_pdepth) { 793 case 8: 794 cm.index = 0; 795 cm.count = 16; 796 cm.red = (uint8_t *)cmap4_to_24.red; 797 cm.blue = (uint8_t *)cmap4_to_24.blue; 798 cm.green = (uint8_t *)cmap4_to_24.green; 799 (void) ldi_ioctl(tems.ts_hdl, VIS_PUTCMAP, (intptr_t)&cm, 800 FKIOCTL, credp, &rval); 801 break; 802 } 803 } 804 805 void 806 tem_get_size(ushort_t *r, ushort_t *c, ushort_t *x, ushort_t *y) 807 { 808 mutex_enter(&tems.ts_lock); 809 *r = (ushort_t)tems.ts_c_dimension.height; 810 *c = (ushort_t)tems.ts_c_dimension.width; 811 *x = (ushort_t)tems.ts_p_dimension.width; 812 *y = (ushort_t)tems.ts_p_dimension.height; 813 mutex_exit(&tems.ts_lock); 814 } 815 816 void 817 tem_register_modechg_cb(tem_modechg_cb_t func, tem_modechg_cb_arg_t arg) 818 { 819 mutex_enter(&tems.ts_lock); 820 821 tems.ts_modechg_cb = func; 822 tems.ts_modechg_arg = arg; 823 824 mutex_exit(&tems.ts_lock); 825 } 826 827 /* 828 * This function is to scroll up the OBP output, which has 829 * different screen height and width with our kernel console. 830 */ 831 static void 832 tem_prom_scroll_up(struct tem_vt_state *tem, int nrows, cred_t *credp, 833 enum called_from called_from) 834 { 835 struct vis_conscopy ma; 836 int ncols, width; 837 838 /* copy */ 839 ma.s_row = nrows * tems.ts_font.vf_height; 840 ma.e_row = tems.ts_p_dimension.height - 1; 841 ma.t_row = 0; 842 843 ma.s_col = 0; 844 ma.e_col = tems.ts_p_dimension.width - 1; 845 ma.t_col = 0; 846 847 tems_safe_copy(&ma, credp, called_from); 848 849 /* clear */ 850 width = tems.ts_font.vf_width; 851 ncols = (tems.ts_p_dimension.width + (width - 1))/ width; 852 853 tem_safe_pix_cls_range(tem, 0, nrows, tems.ts_p_offset.y, 854 0, ncols, 0, B_TRUE, credp, called_from); 855 } 856 857 #define PROM_DEFAULT_FONT_HEIGHT 22 858 #define PROM_DEFAULT_WINDOW_TOP 0x8a 859 860 /* 861 * This function is to compute the starting row of the console, according to 862 * PROM cursor's position. Here we have to take different fonts into account. 863 */ 864 static int 865 tem_adjust_row(struct tem_vt_state *tem, int prom_row, cred_t *credp, 866 enum called_from called_from) 867 { 868 int tem_row; 869 int tem_y; 870 int prom_charheight = 0; 871 int prom_window_top = 0; 872 int scroll_up_lines; 873 874 if (tems.ts_display_mode == VIS_TEXT) 875 return (prom_row); 876 877 plat_tem_get_prom_font_size(&prom_charheight, &prom_window_top); 878 if (prom_charheight == 0) 879 prom_charheight = PROM_DEFAULT_FONT_HEIGHT; 880 if (prom_window_top == 0) 881 prom_window_top = PROM_DEFAULT_WINDOW_TOP; 882 883 tem_y = (prom_row + 1) * prom_charheight + prom_window_top - 884 tems.ts_p_offset.y; 885 tem_row = (tem_y + tems.ts_font.vf_height - 1) / 886 tems.ts_font.vf_height - 1; 887 888 if (tem_row < 0) { 889 tem_row = 0; 890 } else if (tem_row >= (tems.ts_c_dimension.height - 1)) { 891 /* 892 * Scroll up the prom outputs if the PROM cursor's position is 893 * below our tem's lower boundary. 894 */ 895 scroll_up_lines = tem_row - 896 (tems.ts_c_dimension.height - 1); 897 tem_prom_scroll_up(tem, scroll_up_lines, credp, called_from); 898 tem_row = tems.ts_c_dimension.height - 1; 899 } 900 901 return (tem_row); 902 } 903 904 void 905 tem_align(struct tem_vt_state *tem, cred_t *credp, 906 enum called_from called_from) 907 { 908 uint32_t row = 0; 909 uint32_t col = 0; 910 911 plat_tem_hide_prom_cursor(); 912 913 /* 914 * We are getting the current cursor position in pixel 915 * mode so that we don't over-write the console output 916 * during boot. 917 */ 918 plat_tem_get_prom_pos(&row, &col); 919 920 /* 921 * Adjust the row if necessary when the font of our 922 * kernel console tem is different with that of prom 923 * tem. 924 */ 925 row = tem_adjust_row(tem, row, credp, called_from); 926 927 /* first line of our kernel console output */ 928 tem->tvs_first_line = row + 1; 929 930 /* re-set and align cursor position */ 931 tem->tvs_s_cursor.row = tem->tvs_c_cursor.row = 932 (screen_pos_t)row; 933 tem->tvs_s_cursor.col = tem->tvs_c_cursor.col = 0; 934 935 /* 936 * When tem is starting up, part of the screen is filled 937 * with information from boot loader and early boot. 938 * For tem, the screen content above current cursor 939 * should be treated as image. 940 */ 941 for (; row > 0; row--) { 942 for (col = 0; col < tems.ts_c_dimension.width; col++) { 943 tem->tvs_screen_rows[row][col].tc_char = 944 TEM_ATTR(TEM_ATTR_IMAGE); 945 } 946 } 947 } 948 949 static void 950 tems_get_inverses(boolean_t *p_inverse, boolean_t *p_inverse_screen) 951 { 952 int i_inverse = 0; 953 int i_inverse_screen = 0; 954 955 plat_tem_get_inverses(&i_inverse, &i_inverse_screen); 956 957 *p_inverse = (i_inverse == 0) ? B_FALSE : B_TRUE; 958 *p_inverse_screen = (i_inverse_screen == 0) ? B_FALSE : B_TRUE; 959 } 960 961 /* 962 * Get the foreground/background color and attributes from the initial 963 * PROM, so that our kernel console can keep the same visual behaviour. 964 */ 965 static void 966 tems_get_initial_color(tem_color_t *pcolor) 967 { 968 boolean_t inverse, inverse_screen; 969 unsigned short flags = 0; 970 uint8_t fg, bg; 971 972 fg = DEFAULT_ANSI_FOREGROUND; 973 bg = DEFAULT_ANSI_BACKGROUND; 974 #ifndef _HAVE_TEM_FIRMWARE 975 /* 976 * _HAVE_TEM_FIRMWARE is defined on SPARC, at this time, the 977 * plat_tem_get_colors() is implemented only on x86. 978 */ 979 980 plat_tem_get_colors(&fg, &bg); 981 #endif 982 pcolor->fg_color.n = fg; 983 pcolor->bg_color.n = bg; 984 985 tems_get_inverses(&inverse, &inverse_screen); 986 if (inverse) 987 flags |= TEM_ATTR_REVERSE; 988 if (inverse_screen) 989 flags |= TEM_ATTR_SCREEN_REVERSE; 990 991 #ifdef _HAVE_TEM_FIRMWARE 992 if (flags != 0) { 993 /* 994 * If either reverse flag is set, the screen is in 995 * white-on-black mode. We set the bold flag to 996 * improve readability. 997 */ 998 flags |= TEM_ATTR_BOLD; 999 } else { 1000 /* 1001 * Otherwise, the screen is in black-on-white mode. 1002 * The SPARC PROM console, which starts in this mode, 1003 * uses the bright white background colour so we 1004 * match it here. 1005 */ 1006 if (pcolor->bg_color.n == ANSI_COLOR_WHITE) 1007 flags |= TEM_ATTR_BRIGHT_BG; 1008 } 1009 #else 1010 if (flags != 0) { 1011 if (pcolor->fg_color.n == ANSI_COLOR_WHITE) 1012 flags |= TEM_ATTR_BRIGHT_BG; 1013 1014 if (pcolor->fg_color.n == ANSI_COLOR_BLACK) 1015 flags &= ~TEM_ATTR_BRIGHT_BG; 1016 } else { 1017 /* 1018 * In case of black on white we want bright white for BG. 1019 */ 1020 if (pcolor->bg_color.n == ANSI_COLOR_WHITE) 1021 flags |= TEM_ATTR_BRIGHT_BG; 1022 } 1023 #endif 1024 1025 pcolor->a_flags = flags; 1026 } 1027 1028 uchar_t 1029 tem_get_fbmode(tem_vt_state_t tem_arg) 1030 { 1031 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg; 1032 1033 uchar_t fbmode; 1034 1035 mutex_enter(&tem->tvs_lock); 1036 fbmode = tem->tvs_fbmode; 1037 mutex_exit(&tem->tvs_lock); 1038 1039 return (fbmode); 1040 } 1041 1042 void 1043 tem_set_fbmode(tem_vt_state_t tem_arg, uchar_t fbmode, cred_t *credp) 1044 { 1045 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg; 1046 1047 mutex_enter(&tems.ts_lock); 1048 mutex_enter(&tem->tvs_lock); 1049 1050 if (fbmode == tem->tvs_fbmode) { 1051 mutex_exit(&tem->tvs_lock); 1052 mutex_exit(&tems.ts_lock); 1053 return; 1054 } 1055 1056 tem->tvs_fbmode = fbmode; 1057 1058 if (tem->tvs_isactive) { 1059 tem_kdsetmode(tem->tvs_fbmode, credp); 1060 if (fbmode == KD_TEXT) 1061 tem_safe_unblank_screen(tem, credp, CALLED_FROM_NORMAL); 1062 } 1063 1064 mutex_exit(&tem->tvs_lock); 1065 mutex_exit(&tems.ts_lock); 1066 } 1067 1068 void 1069 tem_activate(tem_vt_state_t tem_arg, boolean_t unblank, cred_t *credp) 1070 { 1071 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg; 1072 1073 mutex_enter(&tems.ts_lock); 1074 tems.ts_active = tem; 1075 1076 mutex_enter(&tem->tvs_lock); 1077 tem->tvs_isactive = B_TRUE; 1078 1079 tem_kdsetmode(tem->tvs_fbmode, credp); 1080 1081 if (unblank) 1082 tem_safe_unblank_screen(tem, credp, CALLED_FROM_NORMAL); 1083 1084 mutex_exit(&tem->tvs_lock); 1085 mutex_exit(&tems.ts_lock); 1086 } 1087 1088 void 1089 tem_switch(tem_vt_state_t tem_arg1, tem_vt_state_t tem_arg2, cred_t *credp) 1090 { 1091 struct tem_vt_state *cur = (struct tem_vt_state *)tem_arg1; 1092 struct tem_vt_state *tobe = (struct tem_vt_state *)tem_arg2; 1093 1094 mutex_enter(&tems.ts_lock); 1095 mutex_enter(&tobe->tvs_lock); 1096 mutex_enter(&cur->tvs_lock); 1097 1098 tems.ts_active = tobe; 1099 cur->tvs_isactive = B_FALSE; 1100 tobe->tvs_isactive = B_TRUE; 1101 1102 mutex_exit(&cur->tvs_lock); 1103 1104 if (cur->tvs_fbmode != tobe->tvs_fbmode) 1105 tem_kdsetmode(tobe->tvs_fbmode, credp); 1106 1107 if (tobe->tvs_fbmode == KD_TEXT) 1108 tem_safe_unblank_screen(tobe, credp, CALLED_FROM_NORMAL); 1109 1110 mutex_exit(&tobe->tvs_lock); 1111 mutex_exit(&tems.ts_lock); 1112 } 1113