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