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