xref: /illumos-gate/usr/src/uts/i86pc/boot/boot_fb.c (revision 8e6d016f)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2016 Toomas Soome <tsoome@me.com>
14  */
15 
16 /*
17  * dboot and early kernel needs simple putchar(int) interface to implement
18  * printf() support. So we implement simple interface on top of
19  * linear frame buffer, since we can not use tem directly, we are
20  * just borrowing bits from it.
21  *
22  * Note, this implementation is assuming UEFI linear frame buffer and
23  * 32-bit depth, which should not be issue as GOP is supposed to provide those.
24  * At the time of writing, this is the only case for frame buffer anyhow.
25  */
26 
27 #include <sys/types.h>
28 #include <sys/systm.h>
29 #include <sys/multiboot2.h>
30 #include <sys/framebuffer.h>
31 #include <sys/bootinfo.h>
32 #include <sys/boot_console.h>
33 #include <sys/bootconf.h>
34 
35 #define	P2ROUNDUP(x, align)	(-(-(x) & -(align)))
36 #define	MIN(a, b)		((a) < (b) ? (a) : (b))
37 
38 /*
39  * Simplified visual_io data structures from visual_io.h
40  */
41 
42 struct vis_consdisplay {
43 	uint16_t row;		/* Row to display data at */
44 	uint16_t col;		/* Col to display data at */
45 	uint16_t width;		/* Width of data */
46 	uint16_t height;	/* Height of data */
47 	uint8_t  *data;		/* Data to display */
48 };
49 
50 struct vis_conscopy {
51 	uint16_t s_row;		/* Starting row */
52 	uint16_t s_col;		/* Starting col */
53 	uint16_t e_row;		/* Ending row */
54 	uint16_t e_col;		/* Ending col */
55 	uint16_t t_row;		/* Row to move to */
56 	uint16_t t_col;		/* Col to move to */
57 };
58 
59 /* we have built in fonts 12x22, 6x10, 7x14 and depth 32. */
60 #define	MAX_GLYPH	(12 * 22 * 4)
61 
62 static struct font	boot_fb_font; /* set by set_font() */
63 static uint8_t		glyph[MAX_GLYPH];
64 static uint32_t		last_line_size;
65 static fb_info_pixel_coord_t last_line;
66 
67 /* color translation */
68 typedef struct {
69 	uint8_t red[16];
70 	uint8_t green[16];
71 	uint8_t blue[16];
72 } text_cmap_t;
73 
74 /* BEGIN CSTYLED */
75 /*                             Bk  Rd  Gr  Br  Bl  Mg  Cy  Wh */
76 static uint8_t dim_xlate[] = {  1,  5,  3,  7,  2,  6,  4,  8 };
77 static uint8_t brt_xlate[] = {  9, 13, 11, 15, 10, 14, 12,  0 };
78 /* END CSTYLED */
79 
80 static text_cmap_t cmap4_to_24 = {
81 /* BEGIN CSTYLED */
82 /* 0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15
83   Wh+  Bk   Bl   Gr   Cy   Rd   Mg   Br   Wh   Bk+  Bl+  Gr+  Cy+  Rd+  Mg+  Yw */
84   0xff,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x40,0x00,0x00,0x00,0xff,0xff,0xff,
85   0xff,0x00,0x00,0x80,0x80,0x00,0x00,0x80,0x80,0x40,0x00,0xff,0xff,0x00,0x00,0xff,
86   0xff,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x40,0xff,0x00,0xff,0x00,0xff,0x00
87 /* END CSTYLED */
88 };
89 
90 /*
91  * extract data from MB2 framebuffer tag and set up initial frame buffer.
92  */
93 boolean_t
94 xbi_fb_init(struct xboot_info *xbi)
95 {
96 	multiboot_tag_framebuffer_t *tag;
97 	boot_framebuffer_t *xbi_fb;
98 
99 	xbi_fb = (boot_framebuffer_t *)(uintptr_t)xbi->bi_framebuffer;
100 	if (xbi_fb == NULL)
101 		return (B_FALSE);
102 
103 #if !defined(_BOOT)
104 	/* For early kernel, we get cursor position from dboot. */
105 	fb_info.cursor.origin.x = xbi_fb->cursor.origin.x;
106 	fb_info.cursor.origin.y = xbi_fb->cursor.origin.y;
107 	fb_info.cursor.pos.x = xbi_fb->cursor.pos.x;
108 	fb_info.cursor.pos.y = xbi_fb->cursor.pos.y;
109 	fb_info.cursor.visible = xbi_fb->cursor.visible;
110 #endif
111 
112 	tag = (multiboot_tag_framebuffer_t *)(uintptr_t)xbi_fb->framebuffer;
113 	if (tag == NULL) {
114 		return (B_FALSE);
115 	}
116 
117 	fb_info.paddr = tag->framebuffer_common.framebuffer_addr;
118 	fb_info.pitch = tag->framebuffer_common.framebuffer_pitch;
119 	fb_info.depth = tag->framebuffer_common.framebuffer_bpp;
120 	fb_info.bpp = P2ROUNDUP(fb_info.depth, 8) >> 3;
121 	fb_info.screen.x = tag->framebuffer_common.framebuffer_width;
122 	fb_info.screen.y = tag->framebuffer_common.framebuffer_height;
123 	fb_info.fb_size = fb_info.screen.y * fb_info.pitch;
124 
125 	if (fb_info.paddr == 0)
126 		fb_info.fb_type = FB_TYPE_UNKNOWN;
127 
128 	switch (tag->framebuffer_common.framebuffer_type) {
129 	case MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT:
130 		fb_info.fb_type = FB_TYPE_EGA_TEXT;
131 		return (B_FALSE);
132 
133 	case MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED:
134 		if (fb_info.paddr != 0)
135 			fb_info.fb_type = FB_TYPE_INDEXED;
136 		return (B_TRUE);
137 
138 	case MULTIBOOT_FRAMEBUFFER_TYPE_RGB:
139 		if (fb_info.paddr != 0)
140 			fb_info.fb_type = FB_TYPE_RGB;
141 		break;
142 
143 	default:
144 		return (B_FALSE);
145 	}
146 
147 	fb_info.rgb.red.size = tag->u.fb2.framebuffer_red_mask_size;
148 	fb_info.rgb.red.pos = tag->u.fb2.framebuffer_red_field_position;
149 	fb_info.rgb.green.size = tag->u.fb2.framebuffer_green_mask_size;
150 	fb_info.rgb.green.pos = tag->u.fb2.framebuffer_green_field_position;
151 	fb_info.rgb.blue.size = tag->u.fb2.framebuffer_blue_mask_size;
152 	fb_info.rgb.blue.pos = tag->u.fb2.framebuffer_blue_field_position;
153 
154 	return (B_TRUE);
155 }
156 
157 /* set font and pass the data to fb_info */
158 static void
159 boot_fb_set_font(uint16_t height, uint16_t width)
160 {
161 	short h, w;
162 
163 	h = MIN(height, 4096);
164 	w = MIN(width, 4096);
165 
166 	set_font(&boot_fb_font, (short *)&fb_info.terminal.y,
167 	    (short *)&fb_info.terminal.x, h, w);
168 	fb_info.font_width = boot_fb_font.width;
169 	fb_info.font_height = boot_fb_font.height;
170 }
171 
172 /* fill framebuffer */
173 static void
174 boot_fb_fill(uint8_t *dst, uint32_t data, uint32_t len)
175 {
176 	uint16_t *dst16;
177 	uint32_t *dst32;
178 	uint32_t i;
179 
180 	switch (fb_info.depth) {
181 	case 24:
182 	case 8:
183 		for (i = 0; i < len; i++)
184 			dst[i] = (uint8_t)data;
185 		break;
186 	case 15:
187 	case 16:
188 		dst16 = (uint16_t *)dst;
189 		len /= 2;
190 		for (i = 0; i < len; i++) {
191 			dst16[i] = (uint16_t)data;
192 		}
193 		break;
194 	case 32:
195 		dst32 = (uint32_t *)dst;
196 		len /= 4;
197 		for (i = 0; i < len; i++) {
198 			dst32[i] = data;
199 		}
200 		break;
201 	}
202 }
203 
204 /* copy data to framebuffer */
205 static void
206 boot_fb_cpy(uint8_t *dst, uint8_t *src, uint32_t len)
207 {
208 	uint16_t *dst16, *src16;
209 	uint32_t *dst32, *src32;
210 	uint32_t i;
211 
212 	switch (fb_info.depth) {
213 	case 24:
214 	case 8:
215 		for (i = 0; i < len; i++)
216 			dst[i] = src[i];
217 		break;
218 	case 15:
219 	case 16:
220 		dst16 = (uint16_t *)dst;
221 		src16 = (uint16_t *)src;
222 		for (i = 0; i < len >> 1; i++) {
223 			dst16[i] = src16[i];
224 		}
225 		break;
226 	case 32:
227 		dst32 = (uint32_t *)dst;
228 		src32 = (uint32_t *)src;
229 		for (i = 0; i < len >> 2; i++) {
230 			dst32[i] = src32[i];
231 		}
232 		break;
233 	}
234 }
235 
236 /*
237  * Allocate shadow frame buffer, called from fakebop.c when early boot
238  * allocator is ready.
239  */
240 void
241 boot_fb_shadow_init(bootops_t *bops)
242 {
243 	if (boot_console_type(NULL) != CONS_FRAMEBUFFER)
244 		return;			/* nothing to do */
245 
246 	fb_info.shadow_fb = (uint8_t *)bops->bsys_alloc(NULL, NULL,
247 	    fb_info.fb_size, MMU_PAGESIZE);
248 
249 	if (fb_info.shadow_fb == NULL)
250 		return;
251 
252 	/* Copy FB to shadow */
253 	boot_fb_cpy(fb_info.shadow_fb, fb_info.fb, fb_info.fb_size);
254 }
255 
256 /*
257  * Translate ansi color based on inverses and brightness.
258  */
259 static void
260 boot_get_color(uint32_t *fg, uint32_t *bg)
261 {
262 	/* ansi to solaris colors, see also boot_console.c */
263 	if (fb_info.inverse == B_TRUE ||
264 	    fb_info.inverse_screen == B_TRUE) {
265 		*bg = dim_xlate[fb_info.fg_color];
266 		*fg = brt_xlate[fb_info.bg_color];
267 	} else {
268 		if (fb_info.bg_color == 7)
269 			*bg = brt_xlate[fb_info.bg_color];
270 		else
271 			*bg = dim_xlate[fb_info.bg_color];
272 		*fg = dim_xlate[fb_info.fg_color];
273 	}
274 }
275 
276 /*
277  * Map indexed color to RGB value.
278  */
279 static uint32_t
280 boot_color_map(uint8_t index)
281 {
282 	uint8_t c;
283 	int pos, size;
284 	uint32_t color;
285 
286 	/* 8bit depth is for indexed colors */
287 	if (fb_info.depth == 8)
288 		return (index);
289 
290 	if (index >= sizeof (cmap4_to_24.red))
291 		index = 0;
292 
293 	c = cmap4_to_24.red[index];
294 	pos = fb_info.rgb.red.pos;
295 	size = fb_info.rgb.red.size;
296 	color = ((c >> 8 - size) & ((1 << size) - 1)) << pos;
297 
298 	c = cmap4_to_24.green[index];
299 	pos = fb_info.rgb.green.pos;
300 	size = fb_info.rgb.green.size;
301 	color |= ((c >> 8 - size) & ((1 << size) - 1)) << pos;
302 
303 	c = cmap4_to_24.blue[index];
304 	pos = fb_info.rgb.blue.pos;
305 	size = fb_info.rgb.blue.size;
306 	color |= ((c >> 8 - size) & ((1 << size) - 1)) << pos;
307 
308 	return (color);
309 }
310 
311 /* set up out simple console. */
312 /*ARGSUSED*/
313 void
314 boot_fb_init(int console)
315 {
316 	fb_info_pixel_coord_t window;
317 
318 	/* frame buffer address is mapped in dboot. */
319 	fb_info.fb = (uint8_t *)(uintptr_t)fb_info.paddr;
320 
321 	boot_fb_set_font(fb_info.screen.y, fb_info.screen.x);
322 	window.x =
323 	    (fb_info.screen.x - fb_info.terminal.x * boot_fb_font.width) / 2;
324 	window.y =
325 	    (fb_info.screen.y - fb_info.terminal.y * boot_fb_font.height) / 2;
326 	fb_info.terminal_origin.x = window.x;
327 	fb_info.terminal_origin.y = window.y;
328 
329 #if defined(_BOOT)
330 	/*
331 	 * Being called from dboot, we can have cursor terminal
332 	 * position passed from boot loader. In such case, fix the
333 	 * cursor screen coords.
334 	 */
335 	if (fb_info.cursor.pos.x != 0 || fb_info.cursor.pos.y != 0) {
336 		fb_info.cursor.origin.x = window.x +
337 		    fb_info.cursor.pos.x * boot_fb_font.width;
338 		fb_info.cursor.origin.y = window.y +
339 		    fb_info.cursor.pos.y * boot_fb_font.height;
340 	}
341 #endif
342 
343 	/* If the cursor terminal position is 0,0 just reset screen coords */
344 	if (fb_info.cursor.pos.x == 0 && fb_info.cursor.pos.y == 0) {
345 		fb_info.cursor.origin.x = window.x;
346 		fb_info.cursor.origin.y = window.y;
347 	}
348 
349 	/*
350 	 * Validate cursor coords with screen/terminal dimensions,
351 	 * if anything is off, reset to 0,0
352 	 */
353 	if (fb_info.cursor.pos.x > fb_info.terminal.x ||
354 	    fb_info.cursor.pos.y > fb_info.terminal.y ||
355 	    fb_info.cursor.origin.x > fb_info.screen.x ||
356 	    fb_info.cursor.origin.y > fb_info.screen.y) {
357 
358 		fb_info.cursor.origin.x = window.x;
359 		fb_info.cursor.origin.y = window.y;
360 		fb_info.cursor.pos.x = 0;
361 		fb_info.cursor.pos.y = 0;
362 	}
363 
364 #if defined(_BOOT)
365 	/* clear the screen if cursor is set to 0,0 */
366 	fb_info.cursor.pos.x = fb_info.cursor.pos.y = 0;
367 	if (console == CONS_FRAMEBUFFER &&
368 	    fb_info.cursor.pos.x == 0 && fb_info.cursor.pos.y == 0) {
369 		uint32_t fg, bg;
370 		int i;
371 
372 		boot_get_color(&fg, &bg);
373 		bg = boot_color_map(bg);
374 
375 		for (i = 0; i < fb_info.screen.y; i++) {
376 			uint8_t *dest = fb_info.fb + i * fb_info.pitch;
377 			boot_fb_fill(dest, bg, fb_info.pitch);
378 		}
379 	}
380 #endif
381 	/* set up pre-calculated last line */
382 	last_line_size = fb_info.terminal.x * boot_fb_font.width *
383 	    fb_info.bpp;
384 	last_line.x = window.x;
385 	last_line.y = window.y + (fb_info.terminal.y - 1) * boot_fb_font.height;
386 
387 }
388 
389 /* copy rectangle to framebuffer. */
390 static void
391 boot_fb_blit(struct vis_consdisplay *rect)
392 {
393 	uint32_t size;	/* write size per scanline */
394 	uint8_t *fbp, *sfbp;	/* fb + calculated offset */
395 	int i;
396 
397 	/* make sure we will not write past FB */
398 	if (rect->col >= fb_info.screen.x ||
399 	    rect->row >= fb_info.screen.y ||
400 	    rect->col + rect->width >= fb_info.screen.x ||
401 	    rect->row + rect->height >= fb_info.screen.y)
402 		return;
403 
404 	size = rect->width * fb_info.bpp;
405 	fbp = fb_info.fb + rect->col * fb_info.bpp +
406 	    rect->row * fb_info.pitch;
407 	if (fb_info.shadow_fb != NULL) {
408 		sfbp = fb_info.shadow_fb + rect->col * fb_info.bpp +
409 		    rect->row * fb_info.pitch;
410 	} else {
411 		sfbp = NULL;
412 	}
413 
414 	/* write all scanlines in rectangle */
415 	for (i = 0; i < rect->height; i++) {
416 		uint8_t *dest = fbp + i * fb_info.pitch;
417 		uint8_t *src = rect->data + i * size;
418 		boot_fb_cpy(dest, src, size);
419 		if (sfbp != NULL) {
420 			dest = sfbp + i * fb_info.pitch;
421 			boot_fb_cpy(dest, src, size);
422 		}
423 	}
424 }
425 
426 static void
427 bit_to_pix(uchar_t c)
428 {
429 	uint32_t fg, bg;
430 
431 	boot_get_color(&fg, &bg);
432 	fg = boot_color_map(fg);
433 	bg = boot_color_map(bg);
434 
435 	switch (fb_info.depth) {
436 	case 8:
437 		font_bit_to_pix8(&boot_fb_font, (uint8_t *)glyph, c, fg, bg);
438 		break;
439 	case 15:
440 	case 16:
441 		font_bit_to_pix16(&boot_fb_font, (uint16_t *)glyph, c,
442 		    (uint16_t)fg, (uint16_t)bg);
443 		break;
444 	case 24:
445 		font_bit_to_pix24(&boot_fb_font, (uint8_t *)glyph, c, fg, bg);
446 		break;
447 	case 32:
448 		font_bit_to_pix32(&boot_fb_font, (uint32_t *)glyph, c, fg, bg);
449 		break;
450 	}
451 }
452 
453 /*
454  * move the terminal window lines [1..y] to [0..y-1] and clear last line.
455  */
456 static void
457 boot_fb_scroll(void)
458 {
459 	struct vis_conscopy c_copy;
460 	uint32_t soffset, toffset;
461 	uint32_t width, height;
462 	uint32_t fg, bg;
463 	uint8_t *src, *dst, *sdst;
464 	int i;
465 
466 	boot_get_color(&fg, &bg);
467 	bg = boot_color_map(bg);
468 
469 	/* support for scrolling. set up the console copy data and last line */
470 	c_copy.s_row = fb_info.terminal_origin.y + boot_fb_font.height;
471 	c_copy.s_col = fb_info.terminal_origin.x;
472 	c_copy.e_row = fb_info.screen.y - fb_info.terminal_origin.y;
473 	c_copy.e_col = fb_info.screen.x - fb_info.terminal_origin.x;
474 	c_copy.t_row = fb_info.terminal_origin.y;
475 	c_copy.t_col = fb_info.terminal_origin.x;
476 
477 	soffset = c_copy.s_col * fb_info.bpp + c_copy.s_row * fb_info.pitch;
478 	toffset = c_copy.t_col * fb_info.bpp + c_copy.t_row * fb_info.pitch;
479 	if (fb_info.shadow_fb != NULL) {
480 		src = fb_info.shadow_fb + soffset;
481 		sdst = fb_info.shadow_fb + toffset;
482 	} else {
483 		src = fb_info.fb + soffset;
484 		sdst = NULL;
485 	}
486 	dst = fb_info.fb + toffset;
487 
488 	width = (c_copy.e_col - c_copy.s_col + 1) * fb_info.bpp;
489 	height = c_copy.e_row - c_copy.s_row + 1;
490 	for (i = 0; i < height; i++) {
491 		uint32_t increment = i * fb_info.pitch;
492 		boot_fb_cpy(dst + increment, src + increment, width);
493 		if (sdst != NULL)
494 			boot_fb_cpy(sdst + increment, src + increment, width);
495 	}
496 
497 	/* now clean up the last line */
498 	toffset = last_line.x * fb_info.bpp + last_line.y * fb_info.pitch;
499 	dst = fb_info.fb + toffset;
500 	if (fb_info.shadow_fb != NULL)
501 		sdst = fb_info.shadow_fb + toffset;
502 
503 	for (i = 0; i < boot_fb_font.height; i++) {
504 		uint8_t *dest = dst + i * fb_info.pitch;
505 		if (fb_info.fb + fb_info.fb_size >= dest + last_line_size)
506 			boot_fb_fill(dest, bg, last_line_size);
507 		if (sdst != NULL) {
508 			dest = sdst + i * fb_info.pitch;
509 			if (fb_info.shadow_fb + fb_info.fb_size >=
510 			    dest + last_line_size) {
511 				boot_fb_fill(dest, bg, last_line_size);
512 			}
513 		}
514 	}
515 }
516 
517 /*
518  * Very simple block cursor. Save space below the cursor and restore
519  * when cursor is invisible.
520  */
521 void
522 boot_fb_cursor(boolean_t visible)
523 {
524 	uint32_t offset, size;
525 	uint32_t *fb32, *sfb32 = NULL;
526 	uint32_t fg, bg;
527 	uint16_t *fb16, *sfb16 = NULL;
528 	uint8_t *fb8, *sfb8 = NULL;
529 	int i, pitch;
530 
531 	if (fb_info.cursor.visible == visible)
532 		return;
533 
534 	boot_get_color(&fg, &bg);
535 	fg = boot_color_map(fg);
536 	bg = boot_color_map(bg);
537 
538 	fb_info.cursor.visible = visible;
539 	pitch = fb_info.pitch;
540 	size = boot_fb_font.width * fb_info.bpp;
541 
542 	/*
543 	 * Build cursor image. We are building mirror image of data on
544 	 * frame buffer by (D xor FG) xor BG.
545 	 */
546 	offset = fb_info.cursor.origin.x * fb_info.bpp +
547 	    fb_info.cursor.origin.y * pitch;
548 	switch (fb_info.depth) {
549 	case 8:
550 		for (i = 0; i < boot_fb_font.height; i++) {
551 			fb8 = fb_info.fb + offset + i * pitch;
552 			if (fb_info.shadow_fb != NULL)
553 				sfb8 = fb_info.shadow_fb + offset + i * pitch;
554 			for (uint32_t j = 0; j < size; j += 1) {
555 				fb8[j] = (fb8[j] ^ (fg & 0xff)) ^ (bg & 0xff);
556 
557 				if (sfb8 == NULL)
558 					continue;
559 
560 				sfb8[j] = (sfb8[j] ^ (fg & 0xff)) ^ (bg & 0xff);
561 			}
562 		}
563 		break;
564 	case 15:
565 	case 16:
566 		for (i = 0; i < boot_fb_font.height; i++) {
567 			fb16 = (uint16_t *)(fb_info.fb + offset + i * pitch);
568 			if (fb_info.shadow_fb != NULL)
569 				sfb16 = (uint16_t *)
570 				    (fb_info.shadow_fb + offset + i * pitch);
571 			for (int j = 0; j < boot_fb_font.width; j++) {
572 				fb16[j] = (fb16[j] ^ (fg & 0xffff)) ^
573 				    (bg & 0xffff);
574 
575 				if (sfb16 == NULL)
576 					continue;
577 
578 				sfb16[j] = (sfb16[j] ^ (fg & 0xffff)) ^
579 				    (bg & 0xffff);
580 			}
581 		}
582 		break;
583 	case 24:
584 		for (i = 0; i < boot_fb_font.height; i++) {
585 			fb8 = fb_info.fb + offset + i * pitch;
586 			if (fb_info.shadow_fb != NULL)
587 				sfb8 = fb_info.shadow_fb + offset + i * pitch;
588 			for (uint32_t j = 0; j < size; j += 3) {
589 				fb8[j] = (fb8[j] ^ ((fg >> 16) & 0xff)) ^
590 				    ((bg >> 16) & 0xff);
591 				fb8[j+1] = (fb8[j+1] ^ ((fg >> 8) & 0xff)) ^
592 				    ((bg >> 8) & 0xff);
593 				fb8[j+2] = (fb8[j+2] ^ (fg & 0xff)) ^
594 				    (bg & 0xff);
595 
596 				if (sfb8 == NULL)
597 					continue;
598 
599 				sfb8[j] = (sfb8[j] ^ ((fg >> 16) & 0xff)) ^
600 				    ((bg >> 16) & 0xff);
601 				sfb8[j+1] = (sfb8[j+1] ^ ((fg >> 8) & 0xff)) ^
602 				    ((bg >> 8) & 0xff);
603 				sfb8[j+2] = (sfb8[j+2] ^ (fg & 0xff)) ^
604 				    (bg & 0xff);
605 			}
606 		}
607 		break;
608 	case 32:
609 		for (i = 0; i < boot_fb_font.height; i++) {
610 			fb32 = (uint32_t *)(fb_info.fb + offset + i * pitch);
611 			if (fb_info.shadow_fb != NULL) {
612 				sfb32 = (uint32_t *)
613 				    (fb_info.shadow_fb + offset + i * pitch);
614 			}
615 			for (int j = 0; j < boot_fb_font.width; j++) {
616 				fb32[j] = (fb32[j] ^ fg) ^ bg;
617 
618 				if (sfb32 == NULL)
619 					continue;
620 
621 				sfb32[j] = (sfb32[j] ^ fg) ^ bg;
622 			}
623 		}
624 		break;
625 	}
626 }
627 
628 static void
629 set_cursor_row(void)
630 {
631 	fb_info.cursor.pos.y++;
632 	fb_info.cursor.pos.x = 0;
633 	fb_info.cursor.origin.x = fb_info.terminal_origin.x;
634 
635 	if (fb_info.cursor.pos.y < fb_info.terminal.y &&
636 	    fb_info.cursor.origin.y + boot_fb_font.height < fb_info.screen.y) {
637 		fb_info.cursor.origin.y += boot_fb_font.height;
638 	} else {
639 		fb_info.cursor.pos.y = fb_info.terminal.y - 1;
640 		/* fix the cursor origin y */
641 		fb_info.cursor.origin.y = fb_info.terminal_origin.y +
642 		    boot_fb_font.height * fb_info.cursor.pos.y;
643 		boot_fb_scroll();
644 	}
645 }
646 
647 static void
648 set_cursor_col(void)
649 {
650 	fb_info.cursor.pos.x++;
651 	if (fb_info.cursor.pos.x < fb_info.terminal.x &&
652 	    fb_info.cursor.origin.x + boot_fb_font.width < fb_info.screen.x) {
653 		fb_info.cursor.origin.x += boot_fb_font.width;
654 	} else {
655 		fb_info.cursor.pos.x = 0;
656 		fb_info.cursor.origin.x = fb_info.terminal_origin.x;
657 		set_cursor_row();
658 	}
659 }
660 
661 void
662 boot_fb_putchar(uint8_t c)
663 {
664 	struct vis_consdisplay display;
665 	boolean_t bs = B_FALSE;
666 
667 	/* early tem startup will switch cursor off, if so, keep it off  */
668 	boot_fb_cursor(B_FALSE);	/* cursor off */
669 	switch (c) {
670 	case '\n':
671 		set_cursor_row();
672 		boot_fb_cursor(B_TRUE);
673 		return;
674 	case '\r':
675 		fb_info.cursor.pos.x = 0;
676 		fb_info.cursor.origin.x = fb_info.terminal_origin.x;
677 		boot_fb_cursor(B_TRUE);
678 		return;
679 	case '\b':
680 		if (fb_info.cursor.pos.x > 0) {
681 			fb_info.cursor.pos.x--;
682 			fb_info.cursor.origin.x -= boot_fb_font.width;
683 		}
684 		c = ' ';
685 		bs = B_TRUE;
686 		break;
687 	}
688 
689 	bit_to_pix(c);
690 	display.col = fb_info.cursor.origin.x;
691 	display.row = fb_info.cursor.origin.y;
692 	display.width = boot_fb_font.width;
693 	display.height = boot_fb_font.height;
694 	display.data = glyph;
695 
696 	boot_fb_blit(&display);
697 	if (bs == B_FALSE)
698 		set_cursor_col();
699 	boot_fb_cursor(B_TRUE);
700 }
701