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
_init(void)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
_fini()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
_info(struct modinfo * modinfop)162 _info(struct modinfo *modinfop)
163 {
164 return (mod_info(&modlinkage, modinfop));
165 }
166
167 static void
tem_add(struct tem_vt_state * tem)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
tem_rm(struct tem_vt_state * tem)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
tem_write(tem_vt_state_t tem_arg,uchar_t * buf,ssize_t len,cred_t * credp)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
tem_internal_init(struct tem_vt_state * ptem,cred_t * credp,boolean_t init_color,boolean_t clear_screen)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
tem_initialized(tem_vt_state_t tem_arg)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
tem_init(cred_t * credp,queue_t * rq)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
tem_reinit(struct tem_vt_state * tem,boolean_t reset_display)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
tem_free_buf(struct tem_vt_state * tem)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
tem_destroy(tem_vt_state_t tem_arg,cred_t * credp)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
tems_failed(cred_t * credp,boolean_t finish_ioctl)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
tem_info_init(char * pathname,cred_t * credp)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
tems_check_videomode(struct vis_devinit * tp)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
tems_setup_font(screen_size_t height,screen_size_t width)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
tems_setup_terminal(struct vis_devinit * tp,size_t height,size_t width)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
tems_modechange_callback(struct vis_modechg_arg * arg,struct vis_devinit * devinit)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
tems_cls_layered(struct vis_consclear * pda,cred_t * credp)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
tems_display_layered(struct vis_consdisplay * pda,cred_t * credp)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
tems_copy_layered(struct vis_conscopy * pma,cred_t * credp)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
tems_cursor_layered(struct vis_conscursor * pca,cred_t * credp)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
tem_kdsetmode(int mode,cred_t * credp)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
tems_reset_colormap(cred_t * credp,enum called_from called_from)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
tem_get_size(ushort_t * r,ushort_t * c,ushort_t * x,ushort_t * y)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
tem_register_modechg_cb(tem_modechg_cb_t func,tem_modechg_cb_arg_t arg)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
tem_prom_scroll_up(struct tem_vt_state * tem,int nrows,cred_t * credp,enum called_from called_from)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
tem_adjust_row(struct tem_vt_state * tem,int prom_row,cred_t * credp,enum called_from called_from)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
tem_align(struct tem_vt_state * tem,cred_t * credp,enum called_from called_from)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
tems_get_inverses(boolean_t * p_inverse,boolean_t * p_inverse_screen)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
tems_get_initial_color(tem_color_t * pcolor)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
tem_get_fbmode(tem_vt_state_t tem_arg)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
tem_set_fbmode(tem_vt_state_t tem_arg,uchar_t fbmode,cred_t * credp)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
tem_activate(tem_vt_state_t tem_arg,boolean_t unblank,cred_t * credp)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
tem_switch(tem_vt_state_t tem_arg1,tem_vt_state_t tem_arg2,cred_t * credp)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