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