xref: /illumos-gate/usr/src/uts/common/io/tem.c (revision fe231ea6f3cdffee825d2e92e1a4639b3bc796b7)
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