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