xref: /illumos-gate/usr/src/uts/common/io/tem.c (revision d1aea6f139360e9e7f1504facb24f8521047b15c)
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 	int i, j;
213 	int width, height;
214 	int total;
215 	text_color_t fg;
216 	text_color_t bg;
217 	size_t	tc_size = sizeof (text_color_t);
218 
219 	ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&ptem->tvs_lock));
220 
221 	if (tems.ts_display_mode == VIS_PIXEL) {
222 		ptem->tvs_pix_data_size = tems.ts_pix_data_size;
223 		ptem->tvs_pix_data =
224 		    kmem_alloc(ptem->tvs_pix_data_size, KM_SLEEP);
225 	}
226 
227 	ptem->tvs_outbuf_size = tems.ts_c_dimension.width;
228 	ptem->tvs_outbuf =
229 	    (unsigned char *)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_buf_size = width * height;
234 	ptem->tvs_screen_buf =
235 	    (unsigned char *)kmem_alloc(width * height, KM_SLEEP);
236 
237 	total = width * height * tc_size;
238 	ptem->tvs_fg_buf = (text_color_t *)kmem_alloc(total, KM_SLEEP);
239 	ptem->tvs_bg_buf = (text_color_t *)kmem_alloc(total, KM_SLEEP);
240 	ptem->tvs_color_buf_size = total;
241 
242 	tem_safe_reset_display(ptem, credp, CALLED_FROM_NORMAL,
243 	    clear_screen, init_color);
244 
245 	ptem->tvs_utf8_left = 0;
246 	ptem->tvs_utf8_partial = 0;
247 
248 	tem_safe_get_color(ptem, &fg, &bg, TEM_ATTR_SCREEN_REVERSE);
249 	for (i = 0; i < height; i++)
250 		for (j = 0; j < width; j++) {
251 			ptem->tvs_screen_buf[i * width + j] = ' ';
252 			ptem->tvs_fg_buf[(i * width +j) * tc_size] = fg;
253 			ptem->tvs_bg_buf[(i * width +j) * tc_size] = bg;
254 
255 		}
256 
257 	ptem->tvs_initialized  = 1;
258 }
259 
260 int
261 tem_initialized(tem_vt_state_t tem_arg)
262 {
263 	struct tem_vt_state *ptem = (struct tem_vt_state *)tem_arg;
264 	int ret;
265 
266 	mutex_enter(&ptem->tvs_lock);
267 	ret = ptem->tvs_initialized;
268 	mutex_exit(&ptem->tvs_lock);
269 
270 	return (ret);
271 }
272 
273 tem_vt_state_t
274 tem_init(cred_t *credp)
275 {
276 	struct tem_vt_state *ptem;
277 
278 	ptem = kmem_zalloc(sizeof (struct tem_vt_state), KM_SLEEP);
279 	mutex_init(&ptem->tvs_lock, (char *)NULL, MUTEX_DRIVER, NULL);
280 
281 	mutex_enter(&tems.ts_lock);
282 	mutex_enter(&ptem->tvs_lock);
283 
284 	ptem->tvs_isactive = B_FALSE;
285 	ptem->tvs_fbmode = KD_TEXT;
286 
287 	/*
288 	 * A tem is regarded as initialized only after tem_internal_init(),
289 	 * will be set at the end of tem_internal_init().
290 	 */
291 	ptem->tvs_initialized = 0;
292 
293 
294 	if (!tems.ts_initialized) {
295 		/*
296 		 * Only happens during early console configuration.
297 		 */
298 		tem_add(ptem);
299 		mutex_exit(&ptem->tvs_lock);
300 		mutex_exit(&tems.ts_lock);
301 		return ((tem_vt_state_t)ptem);
302 	}
303 
304 	tem_internal_init(ptem, credp, B_TRUE, B_FALSE);
305 	tem_add(ptem);
306 	mutex_exit(&ptem->tvs_lock);
307 	mutex_exit(&tems.ts_lock);
308 
309 	return ((tem_vt_state_t)ptem);
310 }
311 
312 /*
313  * re-init the tem after video mode has changed and tems_info has
314  * been re-inited. The lock is already held.
315  */
316 static void
317 tem_reinit(struct tem_vt_state *tem, boolean_t reset_display)
318 {
319 	ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock));
320 
321 	tem_free_buf(tem); /* only free virtual buffers */
322 
323 	/* reserve color */
324 	tem_internal_init(tem, kcred, B_FALSE, reset_display);
325 }
326 
327 static void
328 tem_free_buf(struct tem_vt_state *tem)
329 {
330 	ASSERT(tem != NULL && MUTEX_HELD(&tem->tvs_lock));
331 
332 	if (tem->tvs_outbuf != NULL)
333 		kmem_free(tem->tvs_outbuf, tem->tvs_outbuf_size);
334 	if (tem->tvs_pix_data != NULL)
335 		kmem_free(tem->tvs_pix_data, tem->tvs_pix_data_size);
336 	if (tem->tvs_screen_buf != NULL)
337 		kmem_free(tem->tvs_screen_buf, tem->tvs_screen_buf_size);
338 	if (tem->tvs_fg_buf != NULL)
339 		kmem_free(tem->tvs_fg_buf, tem->tvs_color_buf_size);
340 	if (tem->tvs_bg_buf != NULL)
341 		kmem_free(tem->tvs_bg_buf, tem->tvs_color_buf_size);
342 }
343 
344 void
345 tem_destroy(tem_vt_state_t tem_arg, cred_t *credp)
346 {
347 	struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
348 
349 	mutex_enter(&tems.ts_lock);
350 	mutex_enter(&tem->tvs_lock);
351 
352 	if (tem->tvs_isactive && tem->tvs_fbmode == KD_TEXT)
353 		tem_safe_blank_screen(tem, credp, CALLED_FROM_NORMAL);
354 
355 	tem_free_buf(tem);
356 	tem_rm(tem);
357 
358 	if (tems.ts_active == tem)
359 		tems.ts_active = NULL;
360 
361 	mutex_exit(&tem->tvs_lock);
362 	mutex_exit(&tems.ts_lock);
363 
364 	kmem_free(tem, sizeof (struct tem_vt_state));
365 }
366 
367 static int
368 tems_failed(cred_t *credp, boolean_t finish_ioctl)
369 {
370 	int	lyr_rval;
371 
372 	ASSERT(MUTEX_HELD(&tems.ts_lock));
373 
374 	if (finish_ioctl)
375 		(void) ldi_ioctl(tems.ts_hdl, VIS_DEVFINI, 0,
376 		    FWRITE|FKIOCTL, credp, &lyr_rval);
377 
378 	(void) ldi_close(tems.ts_hdl, NULL, credp);
379 	tems.ts_hdl = NULL;
380 	return (ENXIO);
381 }
382 
383 /*
384  * only called once during boot
385  */
386 int
387 tem_info_init(char *pathname, cred_t *credp)
388 {
389 	int			lyr_rval, ret;
390 	struct vis_devinit	temargs;
391 	char			*pathbuf;
392 	size_t height = 0;
393 	size_t width = 0;
394 	struct tem_vt_state *p;
395 
396 	mutex_enter(&tems.ts_lock);
397 
398 	if (tems.ts_initialized) {
399 		mutex_exit(&tems.ts_lock);
400 		return (0);
401 	}
402 
403 	/*
404 	 * Open the layered device using the devfs physical device name
405 	 * after adding the /devices prefix.
406 	 */
407 	pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
408 	(void) strcpy(pathbuf, "/devices");
409 	if (i_ddi_prompath_to_devfspath(pathname,
410 	    pathbuf + strlen("/devices")) != DDI_SUCCESS) {
411 		cmn_err(CE_WARN, "terminal-emulator:  path conversion error");
412 		kmem_free(pathbuf, MAXPATHLEN);
413 
414 		mutex_exit(&tems.ts_lock);
415 		return (ENXIO);
416 	}
417 	if (ldi_open_by_name(pathbuf, FWRITE, credp,
418 	    &tems.ts_hdl, term_li) != 0) {
419 		cmn_err(CE_WARN, "terminal-emulator:  device path open error");
420 		kmem_free(pathbuf, MAXPATHLEN);
421 
422 		mutex_exit(&tems.ts_lock);
423 		return (ENXIO);
424 	}
425 	kmem_free(pathbuf, MAXPATHLEN);
426 
427 	temargs.modechg_cb  = (vis_modechg_cb_t)tems_modechange_callback;
428 	temargs.modechg_arg = NULL;
429 
430 	/*
431 	 * Initialize the console and get the device parameters
432 	 */
433 	if (ldi_ioctl(tems.ts_hdl, VIS_DEVINIT,
434 	    (intptr_t)&temargs, FWRITE|FKIOCTL, credp, &lyr_rval) != 0) {
435 		cmn_err(CE_WARN, "terminal emulator: Compatible fb not found");
436 		ret = tems_failed(credp, B_FALSE);
437 		mutex_exit(&tems.ts_lock);
438 		return (ret);
439 	}
440 
441 	/* Make sure the fb driver and terminal emulator versions match */
442 	if (temargs.version != VIS_CONS_REV) {
443 		cmn_err(CE_WARN,
444 		    "terminal emulator: VIS_CONS_REV %d (see sys/visual_io.h) "
445 		    "of console fb driver not supported", temargs.version);
446 		ret = tems_failed(credp, B_TRUE);
447 		mutex_exit(&tems.ts_lock);
448 		return (ret);
449 	}
450 
451 	if ((tems.ts_fb_polledio = temargs.polledio) == NULL) {
452 		cmn_err(CE_WARN, "terminal emulator: fb doesn't support polled "
453 		    "I/O");
454 		ret = tems_failed(credp, B_TRUE);
455 		mutex_exit(&tems.ts_lock);
456 		return (ret);
457 	}
458 
459 	/* other sanity checks */
460 	if (!((temargs.depth == 4) || (temargs.depth == 8) ||
461 	    (temargs.depth == 24) || (temargs.depth == 32))) {
462 		cmn_err(CE_WARN, "terminal emulator: unsupported depth");
463 		ret = tems_failed(credp, B_TRUE);
464 		mutex_exit(&tems.ts_lock);
465 		return (ret);
466 	}
467 
468 	if ((temargs.mode != VIS_TEXT) && (temargs.mode != VIS_PIXEL)) {
469 		cmn_err(CE_WARN, "terminal emulator: unsupported mode");
470 		ret = tems_failed(credp, B_TRUE);
471 		mutex_exit(&tems.ts_lock);
472 		return (ret);
473 	}
474 
475 	if ((temargs.mode == VIS_PIXEL) && plat_stdout_is_framebuffer())
476 		plat_tem_get_prom_size(&height, &width);
477 
478 	/*
479 	 * Initialize the common terminal emulator info
480 	 */
481 	tems_setup_terminal(&temargs, height, width);
482 
483 	tems_reset_colormap(credp, CALLED_FROM_NORMAL);
484 	tems_get_initial_color(&tems.ts_init_color);
485 
486 	tems.ts_initialized = 1; /* initialization flag */
487 
488 	for (p = list_head(&tems.ts_list); p != NULL;
489 	    p = list_next(&tems.ts_list, p)) {
490 		mutex_enter(&p->tvs_lock);
491 		tem_internal_init(p, credp, B_TRUE, B_FALSE);
492 		if (temargs.mode == VIS_PIXEL)
493 			tem_pix_align(p, credp, CALLED_FROM_NORMAL);
494 		mutex_exit(&p->tvs_lock);
495 	}
496 
497 	mutex_exit(&tems.ts_lock);
498 	return (0);
499 }
500 
501 #define	TEMS_DEPTH_DIFF		0x01
502 #define	TEMS_DIMENSION_DIFF	0x02
503 
504 static uchar_t
505 tems_check_videomode(struct vis_devinit *tp)
506 {
507 	uchar_t result = 0;
508 
509 	if (tems.ts_pdepth != tp->depth)
510 		result |= TEMS_DEPTH_DIFF;
511 
512 	if (tp->mode == VIS_TEXT) {
513 		if (tems.ts_c_dimension.width != tp->width ||
514 		    tems.ts_c_dimension.height != tp->height)
515 			result |= TEMS_DIMENSION_DIFF;
516 	} else {
517 		if (tems.ts_p_dimension.width != tp->width ||
518 		    tems.ts_p_dimension.height != tp->height)
519 			result |= TEMS_DIMENSION_DIFF;
520 	}
521 
522 	return (result);
523 }
524 
525 static void
526 tems_setup_terminal(struct vis_devinit *tp, size_t height, size_t width)
527 {
528 	int i;
529 	int old_blank_buf_size = tems.ts_c_dimension.width;
530 
531 	ASSERT(MUTEX_HELD(&tems.ts_lock));
532 
533 	tems.ts_pdepth = tp->depth;
534 	tems.ts_linebytes = tp->linebytes;
535 	tems.ts_display_mode = tp->mode;
536 
537 	switch (tp->mode) {
538 	case VIS_TEXT:
539 		tems.ts_p_dimension.width = 0;
540 		tems.ts_p_dimension.height = 0;
541 		tems.ts_c_dimension.width = tp->width;
542 		tems.ts_c_dimension.height = tp->height;
543 		tems.ts_callbacks = &tem_safe_text_callbacks;
544 
545 		break;
546 
547 	case VIS_PIXEL:
548 		/*
549 		 * First check to see if the user has specified a screen size.
550 		 * If so, use those values.  Else use 34x80 as the default.
551 		 */
552 		if (width == 0) {
553 			width = TEM_DEFAULT_COLS;
554 			height = TEM_DEFAULT_ROWS;
555 		}
556 		tems.ts_c_dimension.height = (screen_size_t)height;
557 		tems.ts_c_dimension.width = (screen_size_t)width;
558 
559 		tems.ts_p_dimension.height = tp->height;
560 		tems.ts_p_dimension.width = tp->width;
561 
562 		tems.ts_callbacks = &tem_safe_pix_callbacks;
563 
564 		/*
565 		 * set_font() will select a 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 		set_font(&tems.ts_font,
572 		    &tems.ts_c_dimension.height,
573 		    &tems.ts_c_dimension.width,
574 		    tems.ts_p_dimension.height,
575 		    tems.ts_p_dimension.width);
576 
577 		tems.ts_p_offset.y = (tems.ts_p_dimension.height -
578 		    (tems.ts_c_dimension.height * tems.ts_font.height)) / 2;
579 		tems.ts_p_offset.x = (tems.ts_p_dimension.width -
580 		    (tems.ts_c_dimension.width * tems.ts_font.width)) / 2;
581 
582 		tems.ts_pix_data_size =
583 		    tems.ts_font.width * tems.ts_font.height;
584 
585 		tems.ts_pix_data_size *= 4;
586 
587 		tems.ts_pdepth = tp->depth;
588 
589 		break;
590 	}
591 
592 	/* Now virtual cls also uses the blank_line buffer */
593 	if (tems.ts_blank_line)
594 		kmem_free(tems.ts_blank_line, old_blank_buf_size);
595 
596 	tems.ts_blank_line = (unsigned char *)
597 	    kmem_alloc(tems.ts_c_dimension.width, KM_SLEEP);
598 	for (i = 0; i < tems.ts_c_dimension.width; i++)
599 		tems.ts_blank_line[i] = ' ';
600 }
601 
602 /*
603  * This is a callback function that we register with the frame
604  * buffer driver layered underneath.  It gets invoked from
605  * the underlying frame buffer driver to reconfigure the terminal
606  * emulator to a new screen size and depth in conjunction with
607  * framebuffer videomode changes.
608  * Here we keep the foreground/background color and attributes,
609  * which may be different with the initial settings, so that
610  * the color won't change while the framebuffer videomode changes.
611  * And we also reset the kernel terminal emulator and clear the
612  * whole screen.
613  */
614 /* ARGSUSED */
615 void
616 tems_modechange_callback(struct vis_modechg_arg *arg,
617     struct vis_devinit *devinit)
618 {
619 	uchar_t diff;
620 	struct tem_vt_state *p;
621 	tem_modechg_cb_t cb;
622 	tem_modechg_cb_arg_t cb_arg;
623 
624 	ASSERT(!(list_is_empty(&tems.ts_list)));
625 
626 	mutex_enter(&tems.ts_lock);
627 
628 	/*
629 	 * currently only for pixel mode
630 	 */
631 	diff = tems_check_videomode(devinit);
632 	if (diff == 0) {
633 		mutex_exit(&tems.ts_lock);
634 		return;
635 	}
636 
637 	diff = diff & TEMS_DIMENSION_DIFF;
638 
639 	if (diff == 0) {
640 		/*
641 		 * Only need to reinit the active tem.
642 		 */
643 		struct tem_vt_state *active = tems.ts_active;
644 		tems.ts_pdepth = devinit->depth;
645 
646 		mutex_enter(&active->tvs_lock);
647 		ASSERT(active->tvs_isactive);
648 		tem_reinit(active, B_TRUE);
649 		mutex_exit(&active->tvs_lock);
650 
651 		mutex_exit(&tems.ts_lock);
652 		return;
653 	}
654 
655 	tems_setup_terminal(devinit, tems.ts_c_dimension.height,
656 	    tems.ts_c_dimension.width);
657 
658 	for (p = list_head(&tems.ts_list); p != NULL;
659 	    p = list_next(&tems.ts_list, p)) {
660 		mutex_enter(&p->tvs_lock);
661 		tem_reinit(p, p->tvs_isactive);
662 		mutex_exit(&p->tvs_lock);
663 	}
664 
665 
666 	if (tems.ts_modechg_cb == NULL) {
667 		mutex_exit(&tems.ts_lock);
668 		return;
669 	}
670 
671 	cb = tems.ts_modechg_cb;
672 	cb_arg = tems.ts_modechg_arg;
673 
674 	/*
675 	 * Release the lock while doing callback.
676 	 */
677 	mutex_exit(&tems.ts_lock);
678 	cb(cb_arg);
679 }
680 
681 /*
682  * This function is used to display a rectangular blit of data
683  * of a given size and location via the underlying framebuffer driver.
684  * The blit can be as small as a pixel or as large as the screen.
685  */
686 void
687 tems_display_layered(
688 	struct vis_consdisplay *pda,
689 	cred_t *credp)
690 {
691 	int rval;
692 
693 	(void) ldi_ioctl(tems.ts_hdl, VIS_CONSDISPLAY,
694 	    (intptr_t)pda, FKIOCTL, credp, &rval);
695 }
696 
697 /*
698  * This function is used to invoke a block copy operation in the
699  * underlying framebuffer driver.  Rectangle copies are how scrolling
700  * is implemented, as well as horizontal text shifting escape seqs.
701  * such as from vi when deleting characters and words.
702  */
703 void
704 tems_copy_layered(
705 	struct vis_conscopy *pma,
706 	cred_t *credp)
707 {
708 	int rval;
709 
710 	(void) ldi_ioctl(tems.ts_hdl, VIS_CONSCOPY,
711 	    (intptr_t)pma, FKIOCTL, credp, &rval);
712 }
713 
714 /*
715  * This function is used to show or hide a rectangluar monochrom
716  * pixel inverting, text block cursor via the underlying framebuffer.
717  */
718 void
719 tems_cursor_layered(
720 	struct vis_conscursor *pca,
721 	cred_t *credp)
722 {
723 	int rval;
724 
725 	(void) ldi_ioctl(tems.ts_hdl, VIS_CONSCURSOR,
726 	    (intptr_t)pca, FKIOCTL, credp, &rval);
727 }
728 
729 static void
730 tem_kdsetmode(int mode, cred_t *credp)
731 {
732 	int rval;
733 
734 	(void) ldi_ioctl(tems.ts_hdl, KDSETMODE,
735 	    (intptr_t)mode, FKIOCTL, credp, &rval);
736 
737 }
738 
739 static void
740 tems_reset_colormap(cred_t *credp, enum called_from called_from)
741 {
742 	struct vis_cmap cm;
743 	int rval;
744 
745 	if (called_from == CALLED_FROM_STANDALONE)
746 		return;
747 
748 	switch (tems.ts_pdepth) {
749 	case 8:
750 		cm.index = 0;
751 		cm.count = 16;
752 		cm.red   = cmap4_to_24.red;   /* 8-bits (1/3 of TrueColor 24) */
753 		cm.blue  = cmap4_to_24.blue;  /* 8-bits (1/3 of TrueColor 24) */
754 		cm.green = cmap4_to_24.green; /* 8-bits (1/3 of TrueColor 24) */
755 		(void) ldi_ioctl(tems.ts_hdl, VIS_PUTCMAP, (intptr_t)&cm,
756 		    FKIOCTL, credp, &rval);
757 		break;
758 	}
759 }
760 
761 void
762 tem_get_size(ushort_t *r, ushort_t *c, ushort_t *x, ushort_t *y)
763 {
764 	mutex_enter(&tems.ts_lock);
765 	*r = (ushort_t)tems.ts_c_dimension.height;
766 	*c = (ushort_t)tems.ts_c_dimension.width;
767 	*x = (ushort_t)tems.ts_p_dimension.width;
768 	*y = (ushort_t)tems.ts_p_dimension.height;
769 	mutex_exit(&tems.ts_lock);
770 }
771 
772 void
773 tem_register_modechg_cb(tem_modechg_cb_t func, tem_modechg_cb_arg_t arg)
774 {
775 	mutex_enter(&tems.ts_lock);
776 
777 	tems.ts_modechg_cb = func;
778 	tems.ts_modechg_arg = arg;
779 
780 	mutex_exit(&tems.ts_lock);
781 }
782 
783 /*
784  * This function is to scroll up the OBP output, which has
785  * different screen height and width with our kernel console.
786  */
787 static void
788 tem_prom_scroll_up(struct tem_vt_state *tem, int nrows, cred_t *credp,
789     enum called_from called_from)
790 {
791 	struct vis_conscopy	ma;
792 	int	ncols, width;
793 
794 	/* copy */
795 	ma.s_row = nrows * tems.ts_font.height;
796 	ma.e_row = tems.ts_p_dimension.height - 1;
797 	ma.t_row = 0;
798 
799 	ma.s_col = 0;
800 	ma.e_col = tems.ts_p_dimension.width - 1;
801 	ma.t_col = 0;
802 
803 	tems_safe_copy(&ma, credp, called_from);
804 
805 	/* clear */
806 	width = tems.ts_font.width;
807 	ncols = (tems.ts_p_dimension.width + (width - 1))/ width;
808 
809 	tem_safe_pix_cls_range(tem, 0, nrows, tems.ts_p_offset.y,
810 	    0, ncols, 0, B_TRUE, credp, called_from);
811 }
812 
813 #define	PROM_DEFAULT_FONT_HEIGHT	22
814 #define	PROM_DEFAULT_WINDOW_TOP		0x8a
815 
816 /*
817  * This function is to compute the starting row of the console, according to
818  * PROM cursor's position. Here we have to take different fonts into account.
819  */
820 static int
821 tem_adjust_row(struct tem_vt_state *tem, int prom_row, cred_t *credp,
822     enum called_from called_from)
823 {
824 	int	tem_row;
825 	int	tem_y;
826 	int	prom_charheight = 0;
827 	int	prom_window_top = 0;
828 	int	scroll_up_lines;
829 
830 	plat_tem_get_prom_font_size(&prom_charheight, &prom_window_top);
831 	if (prom_charheight == 0)
832 		prom_charheight = PROM_DEFAULT_FONT_HEIGHT;
833 	if (prom_window_top == 0)
834 		prom_window_top = PROM_DEFAULT_WINDOW_TOP;
835 
836 	tem_y = (prom_row + 1) * prom_charheight + prom_window_top -
837 	    tems.ts_p_offset.y;
838 	tem_row = (tem_y + tems.ts_font.height - 1) /
839 	    tems.ts_font.height - 1;
840 
841 	if (tem_row < 0) {
842 		tem_row = 0;
843 	} else if (tem_row >= (tems.ts_c_dimension.height - 1)) {
844 		/*
845 		 * Scroll up the prom outputs if the PROM cursor's position is
846 		 * below our tem's lower boundary.
847 		 */
848 		scroll_up_lines = tem_row -
849 		    (tems.ts_c_dimension.height - 1);
850 		tem_prom_scroll_up(tem, scroll_up_lines, credp, called_from);
851 		tem_row = tems.ts_c_dimension.height - 1;
852 	}
853 
854 	return (tem_row);
855 }
856 
857 void
858 tem_pix_align(struct tem_vt_state *tem, cred_t *credp,
859     enum called_from called_from)
860 {
861 	uint32_t row = 0;
862 	uint32_t col = 0;
863 
864 	if (plat_stdout_is_framebuffer()) {
865 		plat_tem_hide_prom_cursor();
866 
867 		/*
868 		 * We are getting the current cursor position in pixel
869 		 * mode so that we don't over-write the console output
870 		 * during boot.
871 		 */
872 		plat_tem_get_prom_pos(&row, &col);
873 
874 		/*
875 		 * Adjust the row if necessary when the font of our
876 		 * kernel console tem is different with that of prom
877 		 * tem.
878 		 */
879 		row = tem_adjust_row(tem, row, credp, called_from);
880 
881 		/* first line of our kernel console output */
882 		tem->tvs_first_line = row + 1;
883 
884 		/* re-set and align cusror position */
885 		tem->tvs_s_cursor.row = tem->tvs_c_cursor.row =
886 		    (screen_pos_t)row;
887 		tem->tvs_s_cursor.col = tem->tvs_c_cursor.col = 0;
888 	} else {
889 		tem_safe_reset_display(tem, credp, called_from, B_TRUE, B_TRUE);
890 	}
891 }
892 
893 static void
894 tems_get_inverses(boolean_t *p_inverse, boolean_t *p_inverse_screen)
895 {
896 	int i_inverse = 0;
897 	int i_inverse_screen = 0;
898 
899 	plat_tem_get_inverses(&i_inverse, &i_inverse_screen);
900 
901 	*p_inverse = (i_inverse == 0) ? B_FALSE : B_TRUE;
902 	*p_inverse_screen = (i_inverse_screen == 0) ? B_FALSE : B_TRUE;
903 }
904 
905 /*
906  * Get the foreground/background color and attributes from the initial
907  * PROM, so that our kernel console can keep the same visual behaviour.
908  */
909 static void
910 tems_get_initial_color(tem_color_t *pcolor)
911 {
912 	boolean_t inverse, inverse_screen;
913 	unsigned short  flags = 0;
914 
915 	pcolor->fg_color = DEFAULT_ANSI_FOREGROUND;
916 	pcolor->bg_color = DEFAULT_ANSI_BACKGROUND;
917 
918 	if (plat_stdout_is_framebuffer()) {
919 		tems_get_inverses(&inverse, &inverse_screen);
920 		if (inverse)
921 			flags |= TEM_ATTR_REVERSE;
922 		if (inverse_screen)
923 			flags |= TEM_ATTR_SCREEN_REVERSE;
924 
925 		if (flags != 0) {
926 			/*
927 			 * If either reverse flag is set, the screen is in
928 			 * white-on-black mode.  We set the bold flag to
929 			 * improve readability.
930 			 */
931 			flags |= TEM_ATTR_BOLD;
932 		} else {
933 			/*
934 			 * Otherwise, the screen is in black-on-white mode.
935 			 * The SPARC PROM console, which starts in this mode,
936 			 * uses the bright white background colour so we
937 			 * match it here.
938 			 */
939 			if (pcolor->bg_color == ANSI_COLOR_WHITE)
940 				flags |= TEM_ATTR_BRIGHT_BG;
941 		}
942 	}
943 
944 	pcolor->a_flags = flags;
945 }
946 
947 uchar_t
948 tem_get_fbmode(tem_vt_state_t tem_arg)
949 {
950 	struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
951 
952 	uchar_t fbmode;
953 
954 	mutex_enter(&tem->tvs_lock);
955 	fbmode = tem->tvs_fbmode;
956 	mutex_exit(&tem->tvs_lock);
957 
958 	return (fbmode);
959 }
960 
961 void
962 tem_set_fbmode(tem_vt_state_t tem_arg, uchar_t fbmode, cred_t *credp)
963 {
964 	struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
965 
966 	mutex_enter(&tems.ts_lock);
967 	mutex_enter(&tem->tvs_lock);
968 
969 	if (fbmode == tem->tvs_fbmode) {
970 		mutex_exit(&tem->tvs_lock);
971 		mutex_exit(&tems.ts_lock);
972 		return;
973 	}
974 
975 	tem->tvs_fbmode = fbmode;
976 
977 	if (tem->tvs_isactive) {
978 		tem_kdsetmode(tem->tvs_fbmode, credp);
979 		if (fbmode == KD_TEXT)
980 			tem_safe_unblank_screen(tem, credp, CALLED_FROM_NORMAL);
981 	}
982 
983 	mutex_exit(&tem->tvs_lock);
984 	mutex_exit(&tems.ts_lock);
985 }
986 
987 void
988 tem_activate(tem_vt_state_t tem_arg, boolean_t unblank, cred_t *credp)
989 {
990 	struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
991 
992 	mutex_enter(&tems.ts_lock);
993 	tems.ts_active = tem;
994 
995 	mutex_enter(&tem->tvs_lock);
996 	tem->tvs_isactive = B_TRUE;
997 
998 	tem_kdsetmode(tem->tvs_fbmode, credp);
999 
1000 	if (unblank)
1001 		tem_safe_unblank_screen(tem, credp, CALLED_FROM_NORMAL);
1002 
1003 	mutex_exit(&tem->tvs_lock);
1004 	mutex_exit(&tems.ts_lock);
1005 }
1006 
1007 void
1008 tem_switch(tem_vt_state_t tem_arg1, tem_vt_state_t tem_arg2, cred_t *credp)
1009 {
1010 	struct tem_vt_state *cur = (struct tem_vt_state *)tem_arg1;
1011 	struct tem_vt_state *tobe = (struct tem_vt_state *)tem_arg2;
1012 
1013 	mutex_enter(&tems.ts_lock);
1014 	mutex_enter(&tobe->tvs_lock);
1015 	mutex_enter(&cur->tvs_lock);
1016 
1017 	tems.ts_active = tobe;
1018 	cur->tvs_isactive = B_FALSE;
1019 	tobe->tvs_isactive = B_TRUE;
1020 
1021 	mutex_exit(&cur->tvs_lock);
1022 
1023 	if (cur->tvs_fbmode != tobe->tvs_fbmode)
1024 		tem_kdsetmode(tobe->tvs_fbmode, credp);
1025 
1026 	if (tobe->tvs_fbmode == KD_TEXT)
1027 		tem_safe_unblank_screen(tobe, credp, CALLED_FROM_NORMAL);
1028 
1029 	mutex_exit(&tobe->tvs_lock);
1030 	mutex_exit(&tems.ts_lock);
1031 }
1032