xref: /illumos-gate/usr/src/boot/common/gfx_fb.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
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  * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
15  * Copyright 2020 RackTop Systems, Inc.
16  */
17 
18 /*
19  * The workhorse here is gfxfb_blt(). It is implemented to mimic UEFI
20  * GOP Blt, and allows us to fill the rectangle on screen, copy
21  * rectangle from video to buffer and buffer to video and video to video.
22  * Such implementation does allow us to have almost identical implementation
23  * for both BIOS VBE and UEFI.
24  *
25  * ALL pixel data is assumed to be 32-bit BGRA (byte order Blue, Green, Red,
26  * Alpha) format, this allows us to only handle RGB data and not to worry
27  * about mixing RGB with indexed colors.
28  * Data exchange between memory buffer and video will translate BGRA
29  * and native format as following:
30  *
31  * 32-bit to/from 32-bit is trivial case.
32  * 32-bit to/from 24-bit is also simple - we just drop the alpha channel.
33  * 32-bit to/from 16-bit is more complicated, because we nee to handle
34  * data loss from 32-bit to 16-bit. While reading/writing from/to video, we
35  * need to apply masks of 16-bit color components. This will preserve
36  * colors for terminal text. For 32-bit truecolor PMG images, we need to
37  * translate 32-bit colors to 15/16 bit colors and this means data loss.
38  * There are different algorithms how to perform such color space reduction,
39  * we are currently using bitwise right shift to reduce color space and so far
40  * this technique seems to be sufficient (see also gfx_fb_putimage(), the
41  * end of for loop).
42  * 32-bit to/from 8-bit is the most troublesome because 8-bit colors are
43  * indexed. From video, we do get color indexes, and we do translate
44  * color index values to RGB. To write to video, we again need to translate
45  * RGB to color index. Additionally, we need to translate between VGA and
46  * Sun colors.
47  *
48  * Our internal color data is represented using BGRA format. But the hardware
49  * used indexed colors for 8-bit colors (0-255) and for this mode we do
50  * need to perform translation to/from BGRA and index values.
51  *
52  *                   - paletteentry RGB <-> index -
53  * BGRA BUFFER <----/                              \ - VIDEO
54  *                  \                              /
55  *                   -  RGB (16/24/32)            -
56  *
57  * To perform index to RGB translation, we use palette table generated
58  * from when we set up 8-bit mode video. We cannot read palette data from
59  * the hardware, because not all hardware supports reading it.
60  *
61  * BGRA to index is implemented in rgb_to_color_index() by searching
62  * palette array for closest match of RBG values.
63  *
64  * Note: In 8-bit mode, We do store first 16 colors to palette registers
65  * in VGA color order, this serves two purposes; firstly,
66  * if palette update is not supported, we still have correct 16 colors.
67  * Secondly, the kernel does get correct 16 colors when some other boot
68  * loader is used. However, the palette map for 8-bit colors is using
69  * Sun color ordering - this does allow us to skip translation
70  * from VGA colors to Sun colors, while we are reading RGB data.
71  */
72 
73 #include <sys/cdefs.h>
74 #include <sys/param.h>
75 #include <stand.h>
76 #if defined(EFI)
77 #include <efi.h>
78 #include <efilib.h>
79 #else
80 #include <btxv86.h>
81 #include <vbe.h>
82 #endif
83 #include <sys/tem_impl.h>
84 #include <sys/consplat.h>
85 #include <sys/visual_io.h>
86 #include <sys/multiboot2.h>
87 #include <sys/font.h>
88 #include <sys/rgb.h>
89 #include <sys/endian.h>
90 #include <gfx_fb.h>
91 #include <pnglite.h>
92 #include <bootstrap.h>
93 #include <lz4.h>
94 
95 /* VGA text mode does use bold font. */
96 #if !defined(VGA_8X16_FONT)
97 #define	VGA_8X16_FONT		"/boot/fonts/8x16b.fnt"
98 #endif
99 #if !defined(DEFAULT_8X16_FONT)
100 #define	DEFAULT_8X16_FONT	"/boot/fonts/8x16.fnt"
101 #endif
102 
103 /*
104  * Global framebuffer struct, to be updated with mode changes.
105  */
106 multiboot_tag_framebuffer_t gfx_fb;
107 
108 /* To support setenv, keep track of inverses and colors. */
109 static int gfx_inverse = 0;
110 static int gfx_inverse_screen = 0;
111 static uint8_t gfx_fg = DEFAULT_ANSI_FOREGROUND;
112 static uint8_t gfx_bg = DEFAULT_ANSI_BACKGROUND;
113 #if defined(EFI)
114 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *shadow_fb;
115 static EFI_GRAPHICS_OUTPUT_BLT_PIXEL *GlyphBuffer;
116 #else
117 struct paletteentry *shadow_fb;
118 static struct paletteentry *GlyphBuffer;
119 #endif
120 static size_t GlyphBufferSize;
121 
122 int gfx_fb_cons_clear(struct vis_consclear *);
123 void gfx_fb_cons_copy(struct vis_conscopy *);
124 void gfx_fb_cons_display(struct vis_consdisplay *);
125 
126 static bool insert_font(char *, FONT_FLAGS);
127 
128 /*
129  * Set default operations to use bitmap based implementation.
130  * In case of UEFI, if GOP is available, we will switch to GOP based
131  * implementation.
132  *
133  * Also note, for UEFI we do attempt to boost the execution by setting
134  * Task Priority Level (TPL) to TPL_NOTIFY, which is highest priority
135  * usable in application.
136  */
137 
138 /*
139  * Translate platform specific FB address.
140  */
141 static uint8_t *
142 gfx_get_fb_address(void)
143 {
144 	return ((uint8_t *)ptov(gfx_fb.framebuffer_common.framebuffer_addr));
145 }
146 
147 /*
148  * Generic platform callbacks for tem.
149  */
150 void
151 plat_tem_get_prom_font_size(int *charheight, int *windowtop)
152 {
153 	*charheight = 0;
154 	*windowtop = 0;
155 }
156 
157 void
158 plat_tem_get_colors(uint8_t *fg, uint8_t *bg)
159 {
160 	*fg = gfx_fg;
161 	*bg = gfx_bg;
162 }
163 
164 void
165 plat_tem_get_inverses(int *inverse, int *inverse_screen)
166 {
167 	*inverse = gfx_inverse;
168 	*inverse_screen = gfx_inverse_screen;
169 }
170 
171 /*
172  * Utility function to parse gfx mode line strings.
173  */
174 bool
175 gfx_parse_mode_str(char *str, int *x, int *y, int *depth)
176 {
177 	char *p, *end;
178 
179 	errno = 0;
180 	p = str;
181 	*x = strtoul(p, &end, 0);
182 	if (*x == 0 || errno != 0)
183 		return (false);
184 	if (*end != 'x')
185 		return (false);
186 	p = end + 1;
187 	*y = strtoul(p, &end, 0);
188 	if (*y == 0 || errno != 0)
189 		return (false);
190 	if (*end != 'x') {
191 		*depth = -1;    /* auto select */
192 	} else {
193 		p = end + 1;
194 		*depth = strtoul(p, &end, 0);
195 		if (*depth == 0 || errno != 0 || *end != '\0')
196 			return (false);
197 	}
198 
199 	return (true);
200 }
201 
202 uint32_t
203 gfx_fb_color_map(uint8_t index)
204 {
205 	return (rgb_color_map(&rgb_info, index, 0xff));
206 }
207 
208 static bool
209 color_name_to_ansi(const char *name, int *val)
210 {
211 	if (strcasecmp(name, "black") == 0) {
212 		*val = ANSI_COLOR_BLACK;
213 		return (true);
214 	}
215 	if (strcasecmp(name, "red") == 0) {
216 		*val = ANSI_COLOR_RED;
217 		return (true);
218 	}
219 	if (strcasecmp(name, "green") == 0) {
220 		*val = ANSI_COLOR_GREEN;
221 		return (true);
222 	}
223 	if (strcasecmp(name, "yellow") == 0) {
224 		*val = ANSI_COLOR_YELLOW;
225 		return (true);
226 	}
227 	if (strcasecmp(name, "blue") == 0) {
228 		*val = ANSI_COLOR_BLUE;
229 		return (true);
230 	}
231 	if (strcasecmp(name, "magenta") == 0) {
232 		*val = ANSI_COLOR_MAGENTA;
233 		return (true);
234 	}
235 	if (strcasecmp(name, "cyan") == 0) {
236 		*val = ANSI_COLOR_CYAN;
237 		return (true);
238 	}
239 	if (strcasecmp(name, "white") == 0) {
240 		*val = ANSI_COLOR_WHITE;
241 		return (true);
242 	}
243 	return (false);
244 }
245 
246 /* Callback to check and set colors */
247 static int
248 gfx_set_colors(struct env_var *ev, int flags, const void *value)
249 {
250 	int val = 0, limit;
251 	char buf[2];
252 	const void *evalue;
253 
254 	if (value == NULL)
255 		return (CMD_OK);
256 
257 	limit = 255;
258 
259 	if (color_name_to_ansi(value, &val)) {
260 		snprintf(buf, sizeof (buf), "%d", val);
261 		evalue = buf;
262 	} else {
263 		char *end;
264 
265 		errno = 0;
266 		val = (int)strtol(value, &end, 0);
267 		if (errno != 0 || *end != '\0') {
268 			printf("Allowed values are either ansi color name or "
269 			    "number from range [0-255].\n");
270 			return (CMD_OK);
271 		}
272 		evalue = value;
273 	}
274 
275 	/* invalid value? */
276 	if ((val < 0 || val > limit)) {
277 		printf("Allowed values are either ansi color name or "
278 		    "number from range [0-255].\n");
279 		return (CMD_OK);
280 	}
281 
282 	if (strcmp(ev->ev_name, "tem.fg_color") == 0) {
283 		/* is it already set? */
284 		if (gfx_fg == val)
285 			return (CMD_OK);
286 		gfx_fg = val;
287 	}
288 	if (strcmp(ev->ev_name, "tem.bg_color") == 0) {
289 		/* is it already set? */
290 		if (gfx_bg == val)
291 			return (CMD_OK);
292 		gfx_bg = val;
293 	}
294 	env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL);
295 	plat_cons_update_mode(-1);
296 	return (CMD_OK);
297 }
298 
299 /* Callback to check and set inverses */
300 static int
301 gfx_set_inverses(struct env_var *ev, int flags, const void *value)
302 {
303 	int t, f;
304 
305 	if (value == NULL)
306 		return (CMD_OK);
307 
308 	t = strcmp(value, "true");
309 	f = strcmp(value, "false");
310 
311 	/* invalid value? */
312 	if (t != 0 && f != 0)
313 		return (CMD_OK);
314 
315 	if (strcmp(ev->ev_name, "tem.inverse") == 0) {
316 		/* is it already set? */
317 		if (gfx_inverse == (t == 0))
318 			return (CMD_OK);
319 		gfx_inverse = (t == 0);
320 	}
321 	if (strcmp(ev->ev_name, "tem.inverse-screen") == 0) {
322 		/* is it already set? */
323 		if (gfx_inverse_screen == (t == 0))
324 			return (CMD_OK);
325 		gfx_inverse_screen = (t == 0);
326 	}
327 	env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
328 	plat_cons_update_mode(-1);
329 	return (CMD_OK);
330 }
331 
332 /*
333  * Initialize gfx framework.
334  */
335 void
336 gfx_framework_init(void)
337 {
338 	int rc, limit;
339 	char *env, buf[2];
340 
341 	if (gfx_fb.framebuffer_common.framebuffer_bpp < 24)
342 		limit = 7;
343 	else
344 		limit = 255;
345 
346 	/* set up tem inverse controls */
347 	env = getenv("tem.inverse");
348 	if (env != NULL) {
349 		if (strcmp(env, "true") == 0)
350 			gfx_inverse = 1;
351 		unsetenv("tem.inverse");
352 	}
353 
354 	env = getenv("tem.inverse-screen");
355 	if (env != NULL) {
356 		if (strcmp(env, "true") == 0)
357 			gfx_inverse_screen = 1;
358 		unsetenv("tem.inverse-screen");
359 	}
360 
361 	if (gfx_inverse)
362 		env = "true";
363 	else
364 		env = "false";
365 
366 	env_setenv("tem.inverse", EV_VOLATILE, env, gfx_set_inverses,
367 	    env_nounset);
368 
369 	if (gfx_inverse_screen)
370 		env = "true";
371 	else
372 		env = "false";
373 
374 	env_setenv("tem.inverse-screen", EV_VOLATILE, env, gfx_set_inverses,
375 	    env_nounset);
376 
377 	/* set up tem color controls */
378 	env = getenv("tem.fg_color");
379 	if (env != NULL) {
380 		rc = (int)strtol(env, NULL, 0);
381 		if ((rc >= 0 && rc <= limit) && (rc <= 7 || rc >= 16))
382 			gfx_fg = rc;
383 		unsetenv("tem.fg_color");
384 	}
385 
386 	env = getenv("tem.bg_color");
387 	if (env != NULL) {
388 		rc = (int)strtol(env, NULL, 0);
389 		if ((rc >= 0 && rc <= limit) && (rc <= 7 || rc >= 16))
390 			gfx_bg = rc;
391 		unsetenv("tem.bg_color");
392 	}
393 
394 	snprintf(buf, sizeof (buf), "%d", gfx_fg);
395 	env_setenv("tem.fg_color", EV_VOLATILE, buf, gfx_set_colors,
396 	    env_nounset);
397 	snprintf(buf, sizeof (buf), "%d", gfx_bg);
398 	env_setenv("tem.bg_color", EV_VOLATILE, buf, gfx_set_colors,
399 	    env_nounset);
400 
401 	/*
402 	 * Setup font list to have builtin font.
403 	 */
404 	(void) insert_font(NULL, FONT_BUILTIN);
405 }
406 
407 /*
408  * Get indexed color from RGB. This function is used to write data to video
409  * memory when the adapter is set to use indexed colors.
410  * Since UEFI does only support 32-bit colors, we do not implement it for
411  * UEFI because there is no need for it and we do not have palette array
412  * for UEFI.
413  */
414 static uint8_t
415 rgb_to_color_index(uint8_t r, uint8_t g, uint8_t b)
416 {
417 #if !defined(EFI)
418 	uint32_t color, best, dist, k;
419 	int diff;
420 
421 	color = 0;
422 	best = 255 * 255 * 255;
423 	for (k = 0; k < NCMAP; k++) {
424 		diff = r - pe8[k].Red;
425 		dist = diff * diff;
426 		diff = g - pe8[k].Green;
427 		dist += diff * diff;
428 		diff = b - pe8[k].Blue;
429 		dist += diff * diff;
430 
431 		/* Exact match, exit the loop */
432 		if (dist == 0)
433 			break;
434 
435 		if (dist < best) {
436 			color = k;
437 			best = dist;
438 		}
439 	}
440 	if (k == NCMAP)
441 		k = color;
442 	return (k);
443 #else
444 	(void) r;
445 	(void) g;
446 	(void) b;
447 	return (0);
448 #endif
449 }
450 
451 static void
452 gfx_mem_wr1(uint8_t *base, size_t size, uint32_t o, uint8_t v)
453 {
454 
455 	if (o >= size)
456 		return;
457 	*(uint8_t *)(base + o) = v;
458 }
459 
460 static void
461 gfx_mem_wr2(uint8_t *base, size_t size, uint32_t o, uint16_t v)
462 {
463 
464 	if (o >= size)
465 		return;
466 	*(uint16_t *)(base + o) = v;
467 }
468 
469 static void
470 gfx_mem_wr4(uint8_t *base, size_t size, uint32_t o, uint32_t v)
471 {
472 
473 	if (o >= size)
474 		return;
475 	*(uint32_t *)(base + o) = v;
476 }
477 
478 static int
479 gfxfb_blt_fill(void *BltBuffer,
480     uint32_t DestinationX, uint32_t DestinationY,
481     uint32_t Width, uint32_t Height)
482 {
483 #if defined(EFI)
484 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
485 #else
486 	struct paletteentry *p;
487 #endif
488 	uint32_t data, bpp, pitch, y, x;
489 	size_t size;
490 	off_t off;
491 	uint8_t *destination;
492 
493 	if (BltBuffer == NULL)
494 		return (EINVAL);
495 
496 	if (DestinationY + Height >
497 	    gfx_fb.framebuffer_common.framebuffer_height)
498 		return (EINVAL);
499 
500 	if (DestinationX + Width > gfx_fb.framebuffer_common.framebuffer_width)
501 		return (EINVAL);
502 
503 	if (Width == 0 || Height == 0)
504 		return (EINVAL);
505 
506 	p = BltBuffer;
507 	if (gfx_fb.framebuffer_common.framebuffer_bpp == 8) {
508 		data = rgb_to_color_index(p->Red, p->Green, p->Blue);
509 	} else {
510 		data = (p->Red &
511 		    ((1 << gfx_fb.u.fb2.framebuffer_red_mask_size) - 1)) <<
512 		    gfx_fb.u.fb2.framebuffer_red_field_position;
513 		data |= (p->Green &
514 		    ((1 << gfx_fb.u.fb2.framebuffer_green_mask_size) - 1)) <<
515 		    gfx_fb.u.fb2.framebuffer_green_field_position;
516 		data |= (p->Blue &
517 		    ((1 << gfx_fb.u.fb2.framebuffer_blue_mask_size) - 1)) <<
518 		    gfx_fb.u.fb2.framebuffer_blue_field_position;
519 	}
520 
521 	bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
522 	pitch = gfx_fb.framebuffer_common.framebuffer_pitch;
523 	destination = gfx_get_fb_address();
524 	size = gfx_fb.framebuffer_common.framebuffer_height * pitch;
525 
526 	for (y = DestinationY; y < Height + DestinationY; y++) {
527 		off = y * pitch + DestinationX * bpp;
528 		for (x = 0; x < Width; x++) {
529 			switch (bpp) {
530 			case 1:
531 				gfx_mem_wr1(destination, size, off,
532 				    (data < NCOLORS) ?
533 				    solaris_color_to_pc_color[data] : data);
534 				break;
535 			case 2:
536 				gfx_mem_wr2(destination, size, off, data);
537 				break;
538 			case 3:
539 				gfx_mem_wr1(destination, size, off,
540 				    (data >> 16) & 0xff);
541 				gfx_mem_wr1(destination, size, off + 1,
542 				    (data >> 8) & 0xff);
543 				gfx_mem_wr1(destination, size, off + 2,
544 				    data & 0xff);
545 				break;
546 			case 4:
547 				gfx_mem_wr4(destination, size, off, data);
548 				break;
549 			default:
550 				return (EINVAL);
551 			}
552 			off += bpp;
553 		}
554 	}
555 
556 	return (0);
557 }
558 
559 static int
560 gfxfb_blt_video_to_buffer(void *BltBuffer, uint32_t SourceX, uint32_t SourceY,
561     uint32_t DestinationX, uint32_t DestinationY,
562     uint32_t Width, uint32_t Height, uint32_t Delta)
563 {
564 #if defined(EFI)
565 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
566 #else
567 	struct paletteentry *p;
568 #endif
569 	uint32_t x, sy, dy;
570 	uint32_t bpp, pitch, copybytes;
571 	off_t off;
572 	uint8_t *source, *destination, *sb;
573 	uint8_t rm, rp, gm, gp, bm, bp;
574 	bool bgra;
575 
576 	if (BltBuffer == NULL)
577 		return (EINVAL);
578 
579 	if (SourceY + Height >
580 	    gfx_fb.framebuffer_common.framebuffer_height)
581 		return (EINVAL);
582 
583 	if (SourceX + Width > gfx_fb.framebuffer_common.framebuffer_width)
584 		return (EINVAL);
585 
586 	if (Width == 0 || Height == 0)
587 		return (EINVAL);
588 
589 	if (Delta == 0)
590 		Delta = Width * sizeof (*p);
591 
592 	bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
593 	pitch = gfx_fb.framebuffer_common.framebuffer_pitch;
594 
595 	copybytes = Width * bpp;
596 
597 	rm = (1 << gfx_fb.u.fb2.framebuffer_red_mask_size) - 1;
598 	rp = gfx_fb.u.fb2.framebuffer_red_field_position;
599 	gm = (1 << gfx_fb.u.fb2.framebuffer_green_mask_size) - 1;
600 	gp = gfx_fb.u.fb2.framebuffer_green_field_position;
601 	bm = (1 << gfx_fb.u.fb2.framebuffer_blue_mask_size) - 1;
602 	bp = gfx_fb.u.fb2.framebuffer_blue_field_position;
603 	/* If FB pixel format is BGRA, we can use direct copy. */
604 	bgra = bpp == 4 &&
605 	    gfx_fb.u.fb2.framebuffer_red_mask_size == 8 &&
606 	    gfx_fb.u.fb2.framebuffer_red_field_position == 16 &&
607 	    gfx_fb.u.fb2.framebuffer_green_mask_size == 8 &&
608 	    gfx_fb.u.fb2.framebuffer_green_field_position == 8 &&
609 	    gfx_fb.u.fb2.framebuffer_blue_mask_size == 8 &&
610 	    gfx_fb.u.fb2.framebuffer_blue_field_position == 0;
611 
612 	for (sy = SourceY, dy = DestinationY; dy < Height + DestinationY;
613 	    sy++, dy++) {
614 		off = sy * pitch + SourceX * bpp;
615 		source = gfx_get_fb_address() + off;
616 		destination = (uint8_t *)BltBuffer + dy * Delta +
617 		    DestinationX * sizeof (*p);
618 
619 		if (bgra) {
620 			bcopy(source, destination, copybytes);
621 		} else {
622 			for (x = 0; x < Width; x++) {
623 				uint32_t c = 0;
624 
625 				p = (void *)(destination + x * sizeof (*p));
626 				sb = source + x * bpp;
627 				switch (bpp) {
628 				case 1:
629 					c = *sb;
630 					break;
631 				case 2:
632 					c = *(uint16_t *)sb;
633 					break;
634 				case 3:
635 					c = sb[0] << 16 | sb[1] << 8 | sb[2];
636 					break;
637 				case 4:
638 					c = *(uint32_t *)sb;
639 					break;
640 				default:
641 					return (EINVAL);
642 				}
643 
644 				if (bpp == 1) {
645 					*(uint32_t *)p = gfx_fb_color_map(
646 					    (c < NCOLORS) ?
647 					    pc_color_to_solaris_color[c] : c);
648 				} else {
649 					p->Red = (c >> rp) & rm;
650 					p->Green = (c >> gp) & gm;
651 					p->Blue = (c >> bp) & bm;
652 					p->Reserved = 0;
653 				}
654 			}
655 		}
656 	}
657 
658 	return (0);
659 }
660 
661 static int
662 gfxfb_blt_buffer_to_video(void *BltBuffer, uint32_t SourceX, uint32_t SourceY,
663     uint32_t DestinationX, uint32_t DestinationY,
664     uint32_t Width, uint32_t Height, uint32_t Delta)
665 {
666 #if defined(EFI)
667 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
668 #else
669 	struct paletteentry *p;
670 #endif
671 	uint32_t x, sy, dy;
672 	uint32_t bpp, pitch, copybytes;
673 	off_t off;
674 	uint8_t *source, *destination;
675 	uint8_t rm, rp, gm, gp, bm, bp;
676 	bool bgra;
677 
678 	if (BltBuffer == NULL)
679 		return (EINVAL);
680 
681 	if (DestinationY + Height >
682 	    gfx_fb.framebuffer_common.framebuffer_height)
683 		return (EINVAL);
684 
685 	if (DestinationX + Width > gfx_fb.framebuffer_common.framebuffer_width)
686 		return (EINVAL);
687 
688 	if (Width == 0 || Height == 0)
689 		return (EINVAL);
690 
691 	if (Delta == 0)
692 		Delta = Width * sizeof (*p);
693 
694 	bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
695 	pitch = gfx_fb.framebuffer_common.framebuffer_pitch;
696 
697 	copybytes = Width * bpp;
698 
699 	rm = (1 << gfx_fb.u.fb2.framebuffer_red_mask_size) - 1;
700 	rp = gfx_fb.u.fb2.framebuffer_red_field_position;
701 	gm = (1 << gfx_fb.u.fb2.framebuffer_green_mask_size) - 1;
702 	gp = gfx_fb.u.fb2.framebuffer_green_field_position;
703 	bm = (1 << gfx_fb.u.fb2.framebuffer_blue_mask_size) - 1;
704 	bp = gfx_fb.u.fb2.framebuffer_blue_field_position;
705 	/* If FB pixel format is BGRA, we can use direct copy. */
706 	bgra = bpp == 4 &&
707 	    gfx_fb.u.fb2.framebuffer_red_mask_size == 8 &&
708 	    gfx_fb.u.fb2.framebuffer_red_field_position == 16 &&
709 	    gfx_fb.u.fb2.framebuffer_green_mask_size == 8 &&
710 	    gfx_fb.u.fb2.framebuffer_green_field_position == 8 &&
711 	    gfx_fb.u.fb2.framebuffer_blue_mask_size == 8 &&
712 	    gfx_fb.u.fb2.framebuffer_blue_field_position == 0;
713 
714 	for (sy = SourceY, dy = DestinationY; sy < Height + SourceY;
715 	    sy++, dy++) {
716 		off = dy * pitch + DestinationX * bpp;
717 		destination = gfx_get_fb_address() + off;
718 
719 		if (bgra) {
720 			source = (uint8_t *)BltBuffer + sy * Delta +
721 			    SourceX * sizeof (*p);
722 			bcopy(source, destination, copybytes);
723 		} else {
724 			for (x = 0; x < Width; x++) {
725 				uint32_t c;
726 
727 				p = (void *)((uint8_t *)BltBuffer +
728 				    sy * Delta +
729 				    (SourceX + x) * sizeof (*p));
730 				if (bpp == 1) {
731 					c = rgb_to_color_index(p->Red,
732 					    p->Green, p->Blue);
733 				} else {
734 					c = (p->Red & rm) << rp |
735 					    (p->Green & gm) << gp |
736 					    (p->Blue & bm) << bp;
737 				}
738 				off = x * bpp;
739 				switch (bpp) {
740 				case 1:
741 					gfx_mem_wr1(destination, copybytes,
742 					    off, (c < NCOLORS) ?
743 					    solaris_color_to_pc_color[c] : c);
744 					break;
745 				case 2:
746 					gfx_mem_wr2(destination, copybytes,
747 					    off, c);
748 					break;
749 				case 3:
750 					gfx_mem_wr1(destination, copybytes,
751 					    off, (c >> 16) & 0xff);
752 					gfx_mem_wr1(destination, copybytes,
753 					    off + 1, (c >> 8) & 0xff);
754 					gfx_mem_wr1(destination, copybytes,
755 					    off + 2, c & 0xff);
756 					break;
757 				case 4:
758 					gfx_mem_wr4(destination, copybytes,
759 					    off, c);
760 					break;
761 				default:
762 					return (EINVAL);
763 				}
764 			}
765 		}
766 	}
767 
768 	return (0);
769 }
770 
771 static int
772 gfxfb_blt_video_to_video(uint32_t SourceX, uint32_t SourceY,
773     uint32_t DestinationX, uint32_t DestinationY,
774     uint32_t Width, uint32_t Height)
775 {
776 	uint32_t bpp, copybytes;
777 	int pitch;
778 	uint8_t *source, *destination;
779 	off_t off;
780 
781 	if (SourceY + Height >
782 	    gfx_fb.framebuffer_common.framebuffer_height)
783 		return (EINVAL);
784 
785 	if (SourceX + Width > gfx_fb.framebuffer_common.framebuffer_width)
786 		return (EINVAL);
787 
788 	if (DestinationY + Height >
789 	    gfx_fb.framebuffer_common.framebuffer_height)
790 		return (EINVAL);
791 
792 	if (DestinationX + Width > gfx_fb.framebuffer_common.framebuffer_width)
793 		return (EINVAL);
794 
795 	if (Width == 0 || Height == 0)
796 		return (EINVAL);
797 
798 	bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
799 	pitch = gfx_fb.framebuffer_common.framebuffer_pitch;
800 
801 	copybytes = Width * bpp;
802 
803 	off = SourceY * pitch + SourceX * bpp;
804 	source = gfx_get_fb_address() + off;
805 	off = DestinationY * pitch + DestinationX * bpp;
806 	destination = gfx_get_fb_address() + off;
807 
808 	/*
809 	 * To handle overlapping areas, set up reverse copy here.
810 	 */
811 	if ((uintptr_t)destination > (uintptr_t)source) {
812 		source += Height * pitch;
813 		destination += Height * pitch;
814 		pitch = -pitch;
815 	}
816 
817 	while (Height-- > 0) {
818 		bcopy(source, destination, copybytes);
819 		source += pitch;
820 		destination += pitch;
821 	}
822 
823 	return (0);
824 }
825 
826 static void
827 gfxfb_shadow_fill(uint32_t *BltBuffer,
828     uint32_t DestinationX, uint32_t DestinationY,
829     uint32_t Width, uint32_t Height)
830 {
831 	uint32_t fbX, fbY;
832 
833 	if (shadow_fb == NULL)
834 		return;
835 
836 	fbX = gfx_fb.framebuffer_common.framebuffer_width;
837 	fbY = gfx_fb.framebuffer_common.framebuffer_height;
838 
839 	if (BltBuffer == NULL)
840 		return;
841 
842 	if (DestinationX + Width > fbX)
843 		Width = fbX - DestinationX;
844 
845 	if (DestinationY + Height > fbY)
846 		Height = fbY - DestinationY;
847 
848 	uint32_t y2 = Height + DestinationY;
849 	for (uint32_t y1 = DestinationY; y1 < y2; y1++) {
850 		uint32_t off = y1 * fbX + DestinationX;
851 
852 		for (uint32_t x = 0; x < Width; x++) {
853 			*(uint32_t *)&shadow_fb[off + x] = *BltBuffer;
854 		}
855 	}
856 }
857 
858 int
859 gfxfb_blt(void *BltBuffer, GFXFB_BLT_OPERATION BltOperation,
860     uint32_t SourceX, uint32_t SourceY,
861     uint32_t DestinationX, uint32_t DestinationY,
862     uint32_t Width, uint32_t Height, uint32_t Delta)
863 {
864 	int rv;
865 #if defined(EFI)
866 	EFI_STATUS status;
867 	EFI_TPL tpl;
868 	extern EFI_GRAPHICS_OUTPUT *gop;
869 
870 	/*
871 	 * We assume Blt() does work, if not, we will need to build
872 	 * exception list case by case.
873 	 * Once boot services are off, we can not use GOP Blt().
874 	 */
875 	if (gop != NULL && has_boot_services) {
876 		tpl = BS->RaiseTPL(TPL_NOTIFY);
877 		switch (BltOperation) {
878 		case GfxFbBltVideoFill:
879 			gfxfb_shadow_fill(BltBuffer, DestinationX,
880 			    DestinationY, Width, Height);
881 			status = gop->Blt(gop, BltBuffer, EfiBltVideoFill,
882 			    SourceX, SourceY, DestinationX, DestinationY,
883 			    Width, Height, Delta);
884 			break;
885 
886 		case GfxFbBltVideoToBltBuffer:
887 			status = gop->Blt(gop, BltBuffer,
888 			    EfiBltVideoToBltBuffer,
889 			    SourceX, SourceY, DestinationX, DestinationY,
890 			    Width, Height, Delta);
891 			break;
892 
893 		case GfxFbBltBufferToVideo:
894 			status = gop->Blt(gop, BltBuffer, EfiBltBufferToVideo,
895 			    SourceX, SourceY, DestinationX, DestinationY,
896 			    Width, Height, Delta);
897 			break;
898 
899 		case GfxFbBltVideoToVideo:
900 			status = gop->Blt(gop, BltBuffer, EfiBltVideoToVideo,
901 			    SourceX, SourceY, DestinationX, DestinationY,
902 			    Width, Height, Delta);
903 			break;
904 
905 		default:
906 			status = EFI_INVALID_PARAMETER;
907 			break;
908 		}
909 
910 		switch (status) {
911 		case EFI_SUCCESS:
912 			rv = 0;
913 			break;
914 
915 		case EFI_INVALID_PARAMETER:
916 			rv = EINVAL;
917 			break;
918 
919 		case EFI_DEVICE_ERROR:
920 		default:
921 			rv = EIO;
922 			break;
923 		}
924 
925 		BS->RestoreTPL(tpl);
926 		return (rv);
927 	}
928 #endif
929 
930 	switch (BltOperation) {
931 	case GfxFbBltVideoFill:
932 		gfxfb_shadow_fill(BltBuffer, DestinationX, DestinationY,
933 		    Width, Height);
934 		rv = gfxfb_blt_fill(BltBuffer, DestinationX, DestinationY,
935 		    Width, Height);
936 		break;
937 
938 	case GfxFbBltVideoToBltBuffer:
939 		rv = gfxfb_blt_video_to_buffer(BltBuffer, SourceX, SourceY,
940 		    DestinationX, DestinationY, Width, Height, Delta);
941 		break;
942 
943 	case GfxFbBltBufferToVideo:
944 		rv = gfxfb_blt_buffer_to_video(BltBuffer, SourceX, SourceY,
945 		    DestinationX, DestinationY, Width, Height, Delta);
946 		break;
947 
948 	case GfxFbBltVideoToVideo:
949 		rv = gfxfb_blt_video_to_video(SourceX, SourceY,
950 		    DestinationX, DestinationY, Width, Height);
951 		break;
952 
953 	default:
954 		rv = EINVAL;
955 		break;
956 	}
957 	return (rv);
958 }
959 
960 /*
961  * visual io callbacks.
962  */
963 int
964 gfx_fb_cons_clear(struct vis_consclear *ca)
965 {
966 	int rv;
967 	uint32_t width, height;
968 
969 	width = gfx_fb.framebuffer_common.framebuffer_width;
970 	height = gfx_fb.framebuffer_common.framebuffer_height;
971 
972 	rv = gfxfb_blt(&ca->bg_color, GfxFbBltVideoFill, 0, 0,
973 	    0, 0, width, height, 0);
974 
975 	return (rv);
976 }
977 
978 void
979 gfx_fb_cons_copy(struct vis_conscopy *ma)
980 {
981 #if defined(EFI)
982 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *source, *destination;
983 #else
984 	struct paletteentry *source, *destination;
985 #endif
986 	uint32_t width, height, bytes;
987 	uint32_t sx, sy, dx, dy;
988 	uint32_t pitch;
989 	int step;
990 
991 	width = ma->e_col - ma->s_col + 1;
992 	height = ma->e_row - ma->s_row + 1;
993 
994 	sx = ma->s_col;
995 	sy = ma->s_row;
996 	dx = ma->t_col;
997 	dy = ma->t_row;
998 
999 	if (sx + width > gfx_fb.framebuffer_common.framebuffer_width)
1000 		width = gfx_fb.framebuffer_common.framebuffer_width - sx;
1001 
1002 	if (sy + height > gfx_fb.framebuffer_common.framebuffer_height)
1003 		height = gfx_fb.framebuffer_common.framebuffer_height - sy;
1004 
1005 	if (dx + width > gfx_fb.framebuffer_common.framebuffer_width)
1006 		width = gfx_fb.framebuffer_common.framebuffer_width - dx;
1007 
1008 	if (dy + height > gfx_fb.framebuffer_common.framebuffer_height)
1009 		height = gfx_fb.framebuffer_common.framebuffer_height - dy;
1010 
1011 	if (width == 0 || height == 0)
1012 		return;
1013 
1014 	/*
1015 	 * With no shadow fb, use video to video copy.
1016 	 */
1017 	if (shadow_fb == NULL) {
1018 		(void) gfxfb_blt(NULL, GfxFbBltVideoToVideo,
1019 		    sx, sy, dx, dy, width, height, 0);
1020 		return;
1021 	}
1022 
1023 	/*
1024 	 * With shadow fb, we need to copy data on both shadow and video,
1025 	 * to preserve the consistency. We only read data from shadow fb.
1026 	 */
1027 
1028 	step = 1;
1029 	pitch = gfx_fb.framebuffer_common.framebuffer_width;
1030 	bytes = width * sizeof (*shadow_fb);
1031 
1032 	/*
1033 	 * To handle overlapping areas, set up reverse copy here.
1034 	 */
1035 	if (dy * pitch + dx > sy * pitch + sx) {
1036 		sy += height;
1037 		dy += height;
1038 		step = -step;
1039 	}
1040 
1041 	while (height-- > 0) {
1042 		source = &shadow_fb[sy * pitch + sx];
1043 		destination = &shadow_fb[dy * pitch + dx];
1044 
1045 		bcopy(source, destination, bytes);
1046 		(void) gfxfb_blt(destination, GfxFbBltBufferToVideo,
1047 		    0, 0, dx, dy, width, 1, 0);
1048 
1049 		sy += step;
1050 		dy += step;
1051 	}
1052 }
1053 
1054 /*
1055  * Implements alpha blending for RGBA data, could use pixels for arguments,
1056  * but byte stream seems more generic.
1057  * The generic alpha blending is:
1058  * blend = alpha * fg + (1.0 - alpha) * bg.
1059  * Since our alpha is not from range [0..1], we scale appropriately.
1060  */
1061 static uint8_t
1062 alpha_blend(uint8_t fg, uint8_t bg, uint8_t alpha)
1063 {
1064 	uint16_t blend, h, l;
1065 	uint8_t max_alpha;
1066 
1067 	/* 15/16 bit depths have alpha channel size less than 8 */
1068 	max_alpha = (1 << (rgb_info.red.size + rgb_info.green.size +
1069 	    rgb_info.blue.size) / 3) - 1;
1070 
1071 	/* trivial corner cases */
1072 	if (alpha == 0)
1073 		return (bg);
1074 	if (alpha >= max_alpha)
1075 		return (fg);
1076 	blend = (alpha * fg + (max_alpha - alpha) * bg);
1077 	/* Division by max_alpha */
1078 	h = blend >> 8;
1079 	l = blend & max_alpha;
1080 	if (h + l >= max_alpha)
1081 		h++;
1082 	return (h);
1083 }
1084 
1085 /* Copy memory to framebuffer or to memory. */
1086 static void
1087 bitmap_cpy(void *dst, void *src, size_t size)
1088 {
1089 #if defined(EFI)
1090 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ps, *pd;
1091 #else
1092 	struct paletteentry *ps, *pd;
1093 #endif
1094 	uint32_t i;
1095 	uint8_t a;
1096 
1097 	ps = src;
1098 	pd = dst;
1099 
1100 	for (i = 0; i < size; i++) {
1101 		a = ps[i].Reserved;
1102 		pd[i].Red = alpha_blend(ps[i].Red, pd[i].Red, a);
1103 		pd[i].Green = alpha_blend(ps[i].Green, pd[i].Green, a);
1104 		pd[i].Blue = alpha_blend(ps[i].Blue, pd[i].Blue, a);
1105 		pd[i].Reserved = a;
1106 	}
1107 }
1108 
1109 static void *
1110 allocate_glyphbuffer(uint32_t width, uint32_t height)
1111 {
1112 	size_t size;
1113 
1114 	size = sizeof (*GlyphBuffer) * width * height;
1115 	if (size != GlyphBufferSize) {
1116 		free(GlyphBuffer);
1117 		GlyphBuffer = malloc(size);
1118 		if (GlyphBuffer == NULL)
1119 			return (NULL);
1120 		GlyphBufferSize = size;
1121 	}
1122 	return (GlyphBuffer);
1123 }
1124 
1125 void
1126 gfx_fb_cons_display(struct vis_consdisplay *da)
1127 {
1128 #if defined(EFI)
1129 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, *data;
1130 #else
1131 	struct paletteentry *BltBuffer, *data;
1132 #endif
1133 	uint32_t size;
1134 
1135 	/* make sure we will not write past FB */
1136 	if ((uint32_t)da->col >= gfx_fb.framebuffer_common.framebuffer_width ||
1137 	    (uint32_t)da->row >= gfx_fb.framebuffer_common.framebuffer_height ||
1138 	    (uint32_t)da->col + da->width >
1139 	    gfx_fb.framebuffer_common.framebuffer_width ||
1140 	    (uint32_t)da->row + da->height >
1141 	    gfx_fb.framebuffer_common.framebuffer_height)
1142 		return;
1143 
1144 	/*
1145 	 * If we do have shadow fb, we will use shadow to render data,
1146 	 * and copy shadow to video.
1147 	 */
1148 	if (shadow_fb != NULL) {
1149 		uint32_t pitch = gfx_fb.framebuffer_common.framebuffer_width;
1150 		uint32_t dx, dy, width, height;
1151 
1152 		dx = da->col;
1153 		dy = da->row;
1154 		height = da->height;
1155 		width = da->width;
1156 
1157 		data = (void *)da->data;
1158 		/* Copy rectangle line by line. */
1159 		for (uint32_t y = 0; y < height; y++) {
1160 			BltBuffer = shadow_fb + dy * pitch + dx;
1161 			bitmap_cpy(BltBuffer, &data[y * width], width);
1162 			(void) gfxfb_blt(BltBuffer, GfxFbBltBufferToVideo,
1163 			    0, 0, dx, dy, width, 1, 0);
1164 			dy++;
1165 		}
1166 		return;
1167 	}
1168 
1169 	/*
1170 	 * Common data to display is glyph, use preallocated
1171 	 * glyph buffer.
1172 	 */
1173 	if (tems.ts_pix_data_size != GlyphBufferSize)
1174 		(void) allocate_glyphbuffer(da->width, da->height);
1175 
1176 	size = sizeof (*BltBuffer) * da->width * da->height;
1177 	if (size == GlyphBufferSize) {
1178 		BltBuffer = GlyphBuffer;
1179 	} else {
1180 		BltBuffer = malloc(size);
1181 	}
1182 	if (BltBuffer == NULL)
1183 		return;
1184 
1185 	if (gfxfb_blt(BltBuffer, GfxFbBltVideoToBltBuffer,
1186 	    da->col, da->row, 0, 0, da->width, da->height, 0) == 0) {
1187 		bitmap_cpy(BltBuffer, da->data, da->width * da->height);
1188 		(void) gfxfb_blt(BltBuffer, GfxFbBltBufferToVideo,
1189 		    0, 0, da->col, da->row, da->width, da->height, 0);
1190 	}
1191 
1192 	if (BltBuffer != GlyphBuffer)
1193 		free(BltBuffer);
1194 }
1195 
1196 static void
1197 gfx_fb_cursor_impl(void *buf, uint32_t stride, uint32_t fg, uint32_t bg,
1198     struct vis_conscursor *ca)
1199 {
1200 #if defined(EFI)
1201 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
1202 #else
1203 	struct paletteentry *p;
1204 #endif
1205 	union pixel {
1206 #if defined(EFI)
1207 		EFI_GRAPHICS_OUTPUT_BLT_PIXEL p;
1208 #else
1209 		struct paletteentry p;
1210 #endif
1211 		uint32_t p32;
1212 	} *row;
1213 
1214 	p = buf;
1215 
1216 	/*
1217 	 * Build inverse image of the glyph.
1218 	 * Since xor has self-inverse property, drawing cursor
1219 	 * second time on the same spot, will restore the original content.
1220 	 */
1221 	for (screen_size_t i = 0; i < ca->height; i++) {
1222 		row = (union pixel *)(p + i * stride);
1223 		for (screen_size_t j = 0; j < ca->width; j++) {
1224 			row[j].p32 = (row[j].p32 ^ fg) ^ bg;
1225 		}
1226 	}
1227 }
1228 
1229 void
1230 gfx_fb_display_cursor(struct vis_conscursor *ca)
1231 {
1232 	union pixel {
1233 #if defined(EFI)
1234 		EFI_GRAPHICS_OUTPUT_BLT_PIXEL p;
1235 #else
1236 		struct paletteentry p;
1237 #endif
1238 		uint32_t p32;
1239 	} fg, bg;
1240 
1241 	bcopy(&ca->fg_color, &fg.p32, sizeof (fg.p32));
1242 	bcopy(&ca->bg_color, &bg.p32, sizeof (bg.p32));
1243 
1244 	if (shadow_fb == NULL &&
1245 	    allocate_glyphbuffer(ca->width, ca->height) != NULL) {
1246 		if (gfxfb_blt(GlyphBuffer, GfxFbBltVideoToBltBuffer,
1247 		    ca->col, ca->row, 0, 0, ca->width, ca->height, 0) == 0)
1248 			gfx_fb_cursor_impl(GlyphBuffer, ca->width,
1249 			    fg.p32, bg.p32, ca);
1250 
1251 		(void) gfxfb_blt(GlyphBuffer, GfxFbBltBufferToVideo, 0, 0,
1252 		    ca->col, ca->row, ca->width, ca->height, 0);
1253 		return;
1254 	}
1255 
1256 	uint32_t pitch = gfx_fb.framebuffer_common.framebuffer_width;
1257 	uint32_t dx, dy, width, height;
1258 
1259 	dx = ca->col;
1260 	dy = ca->row;
1261 	width = ca->width;
1262 	height = ca->height;
1263 
1264 	gfx_fb_cursor_impl(shadow_fb + dy * pitch + dx, pitch,
1265 	    fg.p32, bg.p32, ca);
1266 	/* Copy rectangle line by line. */
1267 	for (uint32_t y = 0; y < height; y++) {
1268 		(void) gfxfb_blt(shadow_fb + dy * pitch + dx,
1269 		    GfxFbBltBufferToVideo, 0, 0, dx, dy, width, 1, 0);
1270 		dy++;
1271 	}
1272 }
1273 
1274 /*
1275  * Public graphics primitives.
1276  */
1277 
1278 static int
1279 isqrt(int num)
1280 {
1281 	int res = 0;
1282 	int bit = 1 << 30;
1283 
1284 	/* "bit" starts at the highest power of four <= the argument. */
1285 	while (bit > num)
1286 		bit >>= 2;
1287 
1288 	while (bit != 0) {
1289 		if (num >= res + bit) {
1290 			num -= res + bit;
1291 			res = (res >> 1) + bit;
1292 		} else
1293 			res >>= 1;
1294 		bit >>= 2;
1295 	}
1296 	return (res);
1297 }
1298 
1299 /* set pixel in framebuffer using gfx coordinates */
1300 void
1301 gfx_fb_setpixel(uint32_t x, uint32_t y)
1302 {
1303 	text_color_t fg, bg;
1304 
1305 	if (plat_stdout_is_framebuffer() == 0)
1306 		return;
1307 
1308 	tem_get_colors((tem_vt_state_t)tems.ts_active, &fg, &bg);
1309 
1310 	if (x >= gfx_fb.framebuffer_common.framebuffer_width ||
1311 	    y >= gfx_fb.framebuffer_common.framebuffer_height)
1312 		return;
1313 
1314 	gfxfb_blt(&fg.n, GfxFbBltVideoFill, 0, 0, x, y, 1, 1, 0);
1315 }
1316 
1317 /*
1318  * draw rectangle in framebuffer using gfx coordinates.
1319  */
1320 void
1321 gfx_fb_drawrect(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2,
1322     uint32_t fill)
1323 {
1324 	text_color_t fg, bg;
1325 
1326 	if (plat_stdout_is_framebuffer() == 0)
1327 		return;
1328 
1329 	tem_get_colors((tem_vt_state_t)tems.ts_active, &fg, &bg);
1330 
1331 	if (fill != 0) {
1332 		gfxfb_blt(&fg.n, GfxFbBltVideoFill,
1333 		    0, 0, x1, y1, x2 - x1, y2 - y1, 0);
1334 	} else {
1335 		gfxfb_blt(&fg.n, GfxFbBltVideoFill,
1336 		    0, 0, x1, y1, x2 - x1, 1, 0);
1337 		gfxfb_blt(&fg.n, GfxFbBltVideoFill,
1338 		    0, 0, x1, y2, x2 - x1, 1, 0);
1339 		gfxfb_blt(&fg.n, GfxFbBltVideoFill,
1340 		    0, 0, x1, y1, 1, y2 - y1, 0);
1341 		gfxfb_blt(&fg.n, GfxFbBltVideoFill,
1342 		    0, 0, x2, y1, 1, y2 - y1, 0);
1343 	}
1344 }
1345 
1346 void
1347 gfx_fb_line(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t wd)
1348 {
1349 	int dx, sx, dy, sy;
1350 	int err, e2, x2, y2, ed, width;
1351 
1352 	if (plat_stdout_is_framebuffer() == 0)
1353 		return;
1354 
1355 	width = wd;
1356 	sx = x0 < x1? 1 : -1;
1357 	sy = y0 < y1? 1 : -1;
1358 	dx = x1 > x0? x1 - x0 : x0 - x1;
1359 	dy = y1 > y0? y1 - y0 : y0 - y1;
1360 	err = dx + dy;
1361 	ed = dx + dy == 0 ? 1: isqrt(dx * dx + dy * dy);
1362 
1363 	for (;;) {
1364 		gfx_fb_setpixel(x0, y0);
1365 		e2 = err;
1366 		x2 = x0;
1367 		if ((e2 << 1) >= -dx) {		/* x step */
1368 			e2 += dy;
1369 			y2 = y0;
1370 			while (e2 < ed * width &&
1371 			    (y1 != (uint32_t)y2 || dx > dy)) {
1372 				y2 += sy;
1373 				gfx_fb_setpixel(x0, y2);
1374 				e2 += dx;
1375 			}
1376 			if (x0 == x1)
1377 				break;
1378 			e2 = err;
1379 			err -= dy;
1380 			x0 += sx;
1381 		}
1382 		if ((e2 << 1) <= dy) {		/* y step */
1383 			e2 = dx-e2;
1384 			while (e2 < ed * width &&
1385 			    (x1 != (uint32_t)x2 || dx < dy)) {
1386 				x2 += sx;
1387 				gfx_fb_setpixel(x2, y0);
1388 				e2 += dy;
1389 			}
1390 			if (y0 == y1)
1391 				break;
1392 			err += dx;
1393 			y0 += sy;
1394 		}
1395 	}
1396 }
1397 
1398 /*
1399  * quadratic Bézier curve limited to gradients without sign change.
1400  */
1401 void
1402 gfx_fb_bezier(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t x2,
1403     uint32_t y2, uint32_t wd)
1404 {
1405 	int sx, sy, xx, yy, xy, width;
1406 	int dx, dy, err, curvature;
1407 	int i;
1408 
1409 	if (plat_stdout_is_framebuffer() == 0)
1410 		return;
1411 
1412 	width = wd;
1413 	sx = x2 - x1;
1414 	sy = y2 - y1;
1415 	xx = x0 - x1;
1416 	yy = y0 - y1;
1417 	curvature = xx*sy - yy*sx;
1418 
1419 	if (sx*sx + sy*sy > xx*xx+yy*yy) {
1420 		x2 = x0;
1421 		x0 = sx + x1;
1422 		y2 = y0;
1423 		y0 = sy + y1;
1424 		curvature = -curvature;
1425 	}
1426 	if (curvature != 0) {
1427 		xx += sx;
1428 		sx = x0 < x2? 1 : -1;
1429 		xx *= sx;
1430 		yy += sy;
1431 		sy = y0 < y2? 1 : -1;
1432 		yy *= sy;
1433 		xy = (xx*yy) << 1;
1434 		xx *= xx;
1435 		yy *= yy;
1436 		if (curvature * sx * sy < 0) {
1437 			xx = -xx;
1438 			yy = -yy;
1439 			xy = -xy;
1440 			curvature = -curvature;
1441 		}
1442 		dx = 4 * sy * curvature * (x1 - x0) + xx - xy;
1443 		dy = 4 * sx * curvature * (y0 - y1) + yy - xy;
1444 		xx += xx;
1445 		yy += yy;
1446 		err = dx + dy + xy;
1447 		do {
1448 			for (i = 0; i <= width; i++)
1449 				gfx_fb_setpixel(x0 + i, y0);
1450 			if (x0 == x2 && y0 == y2)
1451 				return;  /* last pixel -> curve finished */
1452 			y1 = 2 * err < dx;
1453 			if (2 * err > dy) {
1454 				x0 += sx;
1455 				dx -= xy;
1456 				dy += yy;
1457 				err += dy;
1458 			}
1459 			if (y1 != 0) {
1460 				y0 += sy;
1461 				dy -= xy;
1462 				dx += xx;
1463 				err += dx;
1464 			}
1465 		} while (dy < dx); /* gradient negates -> algorithm fails */
1466 	}
1467 	gfx_fb_line(x0, y0, x2, y2, width);
1468 }
1469 
1470 /*
1471  * draw rectangle using terminal coordinates and current foreground color.
1472  */
1473 void
1474 gfx_term_drawrect(uint32_t ux1, uint32_t uy1, uint32_t ux2, uint32_t uy2)
1475 {
1476 	int x1, y1, x2, y2;
1477 	int xshift, yshift;
1478 	int width, i;
1479 	uint32_t vf_width, vf_height;
1480 
1481 	if (plat_stdout_is_framebuffer() == 0)
1482 		return;
1483 
1484 	vf_width = tems.ts_font.vf_width;
1485 	vf_height = tems.ts_font.vf_height;
1486 	width = vf_width / 4;			/* line width */
1487 	xshift = (vf_width - width) / 2;
1488 	yshift = (vf_height - width) / 2;
1489 
1490 	/* Shift coordinates */
1491 	if (ux1 != 0)
1492 		ux1--;
1493 	if (uy1 != 0)
1494 		uy1--;
1495 	ux2--;
1496 	uy2--;
1497 
1498 	/* mark area used in tem */
1499 	tem_image_display(tems.ts_active, uy1, ux1, uy2 + 1, ux2 + 1);
1500 
1501 	/*
1502 	 * Draw horizontal lines width points thick, shifted from outer edge.
1503 	 */
1504 	x1 = (ux1 + 1) * vf_width + tems.ts_p_offset.x;
1505 	y1 = uy1 * vf_height + tems.ts_p_offset.y + yshift;
1506 	x2 = ux2 * vf_width + tems.ts_p_offset.x;
1507 	gfx_fb_drawrect(x1, y1, x2, y1 + width, 1);
1508 	y2 = uy2 * vf_height + tems.ts_p_offset.y;
1509 	y2 += vf_height - yshift - width;
1510 	gfx_fb_drawrect(x1, y2, x2, y2 + width, 1);
1511 
1512 	/*
1513 	 * Draw vertical lines width points thick, shifted from outer edge.
1514 	 */
1515 	x1 = ux1 * vf_width + tems.ts_p_offset.x + xshift;
1516 	y1 = uy1 * vf_height + tems.ts_p_offset.y;
1517 	y1 += vf_height;
1518 	y2 = uy2 * vf_height + tems.ts_p_offset.y;
1519 	gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
1520 	x1 = ux2 * vf_width + tems.ts_p_offset.x;
1521 	x1 += vf_width - xshift - width;
1522 	gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
1523 
1524 	/* Draw upper left corner. */
1525 	x1 = ux1 * vf_width + tems.ts_p_offset.x + xshift;
1526 	y1 = uy1 * vf_height + tems.ts_p_offset.y;
1527 	y1 += vf_height;
1528 
1529 	x2 = ux1 * vf_width + tems.ts_p_offset.x;
1530 	x2 += vf_width;
1531 	y2 = uy1 * vf_height + tems.ts_p_offset.y + yshift;
1532 	for (i = 0; i <= width; i++)
1533 		gfx_fb_bezier(x1 + i, y1, x1 + i, y2 + i, x2, y2 + i, width-i);
1534 
1535 	/* Draw lower left corner. */
1536 	x1 = ux1 * vf_width + tems.ts_p_offset.x;
1537 	x1 += vf_width;
1538 	y1 = uy2 * vf_height + tems.ts_p_offset.y;
1539 	y1 += vf_height - yshift;
1540 	x2 = ux1 * vf_width + tems.ts_p_offset.x + xshift;
1541 	y2 = uy2 * vf_height + tems.ts_p_offset.y;
1542 	for (i = 0; i <= width; i++)
1543 		gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
1544 
1545 	/* Draw upper right corner. */
1546 	x1 = ux2 * vf_width + tems.ts_p_offset.x;
1547 	y1 = uy1 * vf_height + tems.ts_p_offset.y + yshift;
1548 	x2 = ux2 * vf_width + tems.ts_p_offset.x;
1549 	x2 += vf_width - xshift - width;
1550 	y2 = uy1 * vf_height + tems.ts_p_offset.y;
1551 	y2 += vf_height;
1552 	for (i = 0; i <= width; i++)
1553 		gfx_fb_bezier(x1, y1 + i, x2 + i, y1 + i, x2 + i, y2, width-i);
1554 
1555 	/* Draw lower right corner. */
1556 	x1 = ux2 * vf_width + tems.ts_p_offset.x;
1557 	y1 = uy2 * vf_height + tems.ts_p_offset.y;
1558 	y1 += vf_height - yshift;
1559 	x2 = ux2 * vf_width + tems.ts_p_offset.x;
1560 	x2 += vf_width - xshift - width;
1561 	y2 = uy2 * vf_height + tems.ts_p_offset.y;
1562 	for (i = 0; i <= width; i++)
1563 		gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
1564 }
1565 
1566 int
1567 gfx_fb_putimage(png_t *png, uint32_t ux1, uint32_t uy1, uint32_t ux2,
1568     uint32_t uy2, uint32_t flags)
1569 {
1570 #if defined(EFI)
1571 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
1572 #else
1573 	struct paletteentry *p;
1574 #endif
1575 	struct vis_consdisplay da;
1576 	uint32_t i, j, x, y, fheight, fwidth;
1577 	uint8_t r, g, b, a;
1578 	bool scale = false;
1579 	bool trace = false;
1580 
1581 	trace = (flags & FL_PUTIMAGE_DEBUG) != 0;
1582 
1583 	if (plat_stdout_is_framebuffer() == 0) {
1584 		if (trace)
1585 			printf("Framebuffer not active.\n");
1586 		return (1);
1587 	}
1588 
1589 	if (png->color_type != PNG_TRUECOLOR_ALPHA) {
1590 		if (trace)
1591 			printf("Not truecolor image.\n");
1592 		return (1);
1593 	}
1594 
1595 	if (ux1 > gfx_fb.framebuffer_common.framebuffer_width ||
1596 	    uy1 > gfx_fb.framebuffer_common.framebuffer_height) {
1597 		if (trace)
1598 			printf("Top left coordinate off screen.\n");
1599 		return (1);
1600 	}
1601 
1602 	if (png->width > UINT16_MAX || png->height > UINT16_MAX) {
1603 		if (trace)
1604 			printf("Image too large.\n");
1605 		return (1);
1606 	}
1607 
1608 	if (png->width < 1 || png->height < 1) {
1609 		if (trace)
1610 			printf("Image too small.\n");
1611 		return (1);
1612 	}
1613 
1614 	/*
1615 	 * If 0 was passed for either ux2 or uy2, then calculate the missing
1616 	 * part of the bottom right coordinate.
1617 	 */
1618 	scale = true;
1619 	if (ux2 == 0 && uy2 == 0) {
1620 		/* Both 0, use the native resolution of the image */
1621 		ux2 = ux1 + png->width;
1622 		uy2 = uy1 + png->height;
1623 		scale = false;
1624 	} else if (ux2 == 0) {
1625 		/* Set ux2 from uy2/uy1 to maintain aspect ratio */
1626 		ux2 = ux1 + (png->width * (uy2 - uy1)) / png->height;
1627 	} else if (uy2 == 0) {
1628 		/* Set uy2 from ux2/ux1 to maintain aspect ratio */
1629 		uy2 = uy1 + (png->height * (ux2 - ux1)) / png->width;
1630 	}
1631 
1632 	if (ux2 > gfx_fb.framebuffer_common.framebuffer_width ||
1633 	    uy2 > gfx_fb.framebuffer_common.framebuffer_height) {
1634 		if (trace)
1635 			printf("Bottom right coordinate off screen.\n");
1636 		return (1);
1637 	}
1638 
1639 	fwidth = ux2 - ux1;
1640 	fheight = uy2 - uy1;
1641 
1642 	/*
1643 	 * If the original image dimensions have been passed explicitly,
1644 	 * disable scaling.
1645 	 */
1646 	if (fwidth == png->width && fheight == png->height)
1647 		scale = false;
1648 
1649 	if (ux1 == 0) {
1650 		/*
1651 		 * No top left X co-ordinate (real coordinates start at 1),
1652 		 * place as far right as it will fit.
1653 		 */
1654 		ux2 = gfx_fb.framebuffer_common.framebuffer_width -
1655 		    tems.ts_p_offset.x;
1656 		ux1 = ux2 - fwidth;
1657 	}
1658 
1659 	if (uy1 == 0) {
1660 		/*
1661 		 * No top left Y co-ordinate (real coordinates start at 1),
1662 		 * place as far down as it will fit.
1663 		 */
1664 		uy2 = gfx_fb.framebuffer_common.framebuffer_height -
1665 		    tems.ts_p_offset.y;
1666 		uy1 = uy2 - fheight;
1667 	}
1668 
1669 	if (ux1 >= ux2 || uy1 >= uy2) {
1670 		if (trace)
1671 			printf("Image dimensions reversed.\n");
1672 		return (1);
1673 	}
1674 
1675 	if (fwidth < 2 || fheight < 2) {
1676 		if (trace)
1677 			printf("Target area too small\n");
1678 		return (1);
1679 	}
1680 
1681 	if (trace)
1682 		printf("Image %ux%u -> %ux%u @%ux%u\n",
1683 		    png->width, png->height, fwidth, fheight, ux1, uy1);
1684 
1685 	da.col = ux1;
1686 	da.row = uy1;
1687 	da.width = fwidth;
1688 	da.height = fheight;
1689 
1690 	/*
1691 	 * mark area used in tem
1692 	 */
1693 	if (!(flags & FL_PUTIMAGE_NOSCROLL)) {
1694 		tem_image_display(tems.ts_active,
1695 		    da.row / tems.ts_font.vf_height,
1696 		    da.col / tems.ts_font.vf_width,
1697 		    (da.row + da.height) / tems.ts_font.vf_height,
1698 		    (da.col + da.width) / tems.ts_font.vf_width);
1699 	}
1700 
1701 	if ((flags & FL_PUTIMAGE_BORDER))
1702 		gfx_fb_drawrect(ux1, uy1, ux2, uy2, 0);
1703 
1704 	da.data = malloc(fwidth * fheight * sizeof (*p));
1705 	p = (void *)da.data;
1706 	if (da.data == NULL) {
1707 		if (trace)
1708 			printf("Out of memory.\n");
1709 		return (1);
1710 	}
1711 
1712 	/*
1713 	 * Build image for our framebuffer.
1714 	 */
1715 
1716 	/* Helper to calculate the pixel index from the source png */
1717 #define	GETPIXEL(xx, yy) (((yy) * png->width + (xx)) * png->bpp)
1718 
1719 	/*
1720 	 * For each of the x and y directions, calculate the number of pixels
1721 	 * in the source image that correspond to a single pixel in the target.
1722 	 * Use fixed-point arithmetic with 16-bits for each of the integer and
1723 	 * fractional parts.
1724 	 */
1725 	const uint32_t wcstep = ((png->width - 1) << 16) / (fwidth - 1);
1726 	const uint32_t hcstep = ((png->height - 1) << 16) / (fheight - 1);
1727 
1728 	uint32_t hc = 0;
1729 	for (y = 0; y < fheight; y++) {
1730 		uint32_t hc2 = (hc >> 9) & 0x7f;
1731 		uint32_t hc1 = 0x80 - hc2;
1732 
1733 		uint32_t offset_y = hc >> 16;
1734 		uint32_t offset_y1 = offset_y + 1;
1735 
1736 		uint32_t wc = 0;
1737 		for (x = 0; x < fwidth; x++) {
1738 			uint32_t wc2 = (wc >> 9) & 0x7f;
1739 			uint32_t wc1 = 0x80 - wc2;
1740 
1741 			uint32_t offset_x = wc >> 16;
1742 			uint32_t offset_x1 = offset_x + 1;
1743 
1744 			/* Target pixel index */
1745 			j = y * fwidth + x;
1746 
1747 			if (!scale) {
1748 				i = GETPIXEL(x, y);
1749 				r = png->image[i];
1750 				g = png->image[i + 1];
1751 				b = png->image[i + 2];
1752 				a = png->image[i + 3];
1753 			} else {
1754 				uint8_t pixel[4];
1755 
1756 				uint32_t p00 = GETPIXEL(offset_x, offset_y);
1757 				uint32_t p01 = GETPIXEL(offset_x, offset_y1);
1758 				uint32_t p10 = GETPIXEL(offset_x1, offset_y);
1759 				uint32_t p11 = GETPIXEL(offset_x1, offset_y1);
1760 
1761 				/*
1762 				 * Given a 2x2 array of pixels in the source
1763 				 * image, combine them to produce a single
1764 				 * value for the pixel in the target image.
1765 				 * Each column of pixels is combined using
1766 				 * a weighted average where the top and bottom
1767 				 * pixels contribute hc1 and hc2 respectively.
1768 				 * The calculation for bottom pixel pB and
1769 				 * top pixel pT is:
1770 				 *   (pT * hc1 + pB * hc2) / (hc1 + hc2)
1771 				 * Once the values are determined for the two
1772 				 * columns of pixels, then the columns are
1773 				 * averaged together in the same way but using
1774 				 * wc1 and wc2 for the weightings.
1775 				 *
1776 				 * Since hc1 and hc2 are chosen so that
1777 				 * hc1 + hc2 == 128 (and same for wc1 + wc2),
1778 				 * the >> 14 below is a quick way to divide by
1779 				 * (hc1 + hc2) * (wc1 + wc2)
1780 				 */
1781 				for (i = 0; i < 4; i++)
1782 					pixel[i] = (
1783 					    (png->image[p00 + i] * hc1 +
1784 					    png->image[p01 + i] * hc2) * wc1 +
1785 					    (png->image[p10 + i] * hc1 +
1786 					    png->image[p11 + i] * hc2) * wc2)
1787 					    >> 14;
1788 
1789 				r = pixel[0];
1790 				g = pixel[1];
1791 				b = pixel[2];
1792 				a = pixel[3];
1793 			}
1794 
1795 			if (trace)
1796 				printf("r/g/b: %x/%x/%x\n", r, g, b);
1797 			/*
1798 			 * Rough colorspace reduction for 15/16 bit colors.
1799 			 */
1800 			p[j].Red = r >>
1801 			    (8 - gfx_fb.u.fb2.framebuffer_red_mask_size);
1802 			p[j].Green = g >>
1803 			    (8 - gfx_fb.u.fb2.framebuffer_green_mask_size);
1804 			p[j].Blue = b >>
1805 			    (8 - gfx_fb.u.fb2.framebuffer_blue_mask_size);
1806 			p[j].Reserved = a;
1807 
1808 			wc += wcstep;
1809 		}
1810 		hc += hcstep;
1811 	}
1812 
1813 	gfx_fb_cons_display(&da);
1814 	free(da.data);
1815 	return (0);
1816 }
1817 
1818 /* Return  w^2 + h^2 or 0, if the dimensions are unknown */
1819 static unsigned
1820 edid_diagonal_squared(void)
1821 {
1822 	unsigned w, h;
1823 
1824 	if (edid_info == NULL)
1825 		return (0);
1826 
1827 	w = edid_info->display.max_horizontal_image_size;
1828 	h = edid_info->display.max_vertical_image_size;
1829 
1830 	/* If either one is 0, we have aspect ratio, not size */
1831 	if (w == 0 || h == 0)
1832 		return (0);
1833 
1834 	/*
1835 	 * some monitors encode the aspect ratio instead of the physical size.
1836 	 */
1837 	if ((w == 16 && h == 9) || (w == 16 && h == 10) ||
1838 	    (w == 4 && h == 3) || (w == 5 && h == 4))
1839 		return (0);
1840 
1841 	/*
1842 	 * translate cm to inch, note we scale by 100 here.
1843 	 */
1844 	w = w * 100 / 254;
1845 	h = h * 100 / 254;
1846 
1847 	/* Return w^2 + h^2 */
1848 	return (w * w + h * h);
1849 }
1850 
1851 /*
1852  * calculate pixels per inch.
1853  */
1854 static unsigned
1855 gfx_get_ppi(void)
1856 {
1857 	unsigned dp, di;
1858 
1859 	di = edid_diagonal_squared();
1860 	if (di == 0)
1861 		return (0);
1862 
1863 	dp = gfx_fb.framebuffer_common.framebuffer_width *
1864 	    gfx_fb.framebuffer_common.framebuffer_width +
1865 	    gfx_fb.framebuffer_common.framebuffer_height *
1866 	    gfx_fb.framebuffer_common.framebuffer_height;
1867 
1868 	return (isqrt(dp / di));
1869 }
1870 
1871 /*
1872  * Calculate font size from density independent pixels (dp):
1873  * ((16dp * ppi) / 160) * display_factor.
1874  * Here we are using fixed constants: 1dp == 160 ppi and
1875  * display_factor 2.
1876  *
1877  * We are rounding font size up and are searching for font which is
1878  * not smaller than calculated size value.
1879  */
1880 bitmap_data_t *
1881 gfx_get_font(void)
1882 {
1883 	unsigned ppi, size;
1884 	bitmap_data_t *font = NULL;
1885 	struct fontlist *fl, *next;
1886 
1887 	/* Text mode is not supported here. */
1888 	if (gfx_fb.framebuffer_common.framebuffer_type ==
1889 	    MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT)
1890 		return (NULL);
1891 
1892 	ppi = gfx_get_ppi();
1893 	if (ppi == 0)
1894 		return (NULL);
1895 
1896 	/*
1897 	 * We will search for 16dp font.
1898 	 * We are using scale up by 10 for roundup.
1899 	 */
1900 	size = (16 * ppi * 10) / 160;
1901 	/* Apply display factor 2.  */
1902 	size = roundup(size * 2, 10) / 10;
1903 
1904 	STAILQ_FOREACH(fl, &fonts, font_next) {
1905 		next = STAILQ_NEXT(fl, font_next);
1906 		/*
1907 		 * If this is last font or, if next font is smaller,
1908 		 * we have our font. Make sure, it actually is loaded.
1909 		 */
1910 		if (next == NULL || next->font_data->height < size) {
1911 			font = fl->font_data;
1912 			if (font->font == NULL ||
1913 			    fl->font_flags == FONT_RELOAD) {
1914 				if (fl->font_load != NULL &&
1915 				    fl->font_name != NULL)
1916 					font = fl->font_load(fl->font_name);
1917 			}
1918 			break;
1919 		}
1920 	}
1921 
1922 	return (font);
1923 }
1924 
1925 static int
1926 load_mapping(int fd, struct font *fp, int n)
1927 {
1928 	size_t i, size;
1929 	ssize_t rv;
1930 	struct font_map *mp;
1931 
1932 	if (fp->vf_map_count[n] == 0)
1933 		return (0);
1934 
1935 	size = fp->vf_map_count[n] * sizeof (*mp);
1936 	mp = malloc(size);
1937 	if (mp == NULL)
1938 		return (ENOMEM);
1939 	fp->vf_map[n] = mp;
1940 
1941 	rv = read(fd, mp, size);
1942 	if (rv < 0 || (size_t)rv != size) {
1943 		free(fp->vf_map[n]);
1944 		fp->vf_map[n] = NULL;
1945 		return (EIO);
1946 	}
1947 
1948 	for (i = 0; i < fp->vf_map_count[n]; i++) {
1949 		mp[i].font_src = be32toh(mp[i].font_src);
1950 		mp[i].font_dst = be16toh(mp[i].font_dst);
1951 		mp[i].font_len = be16toh(mp[i].font_len);
1952 	}
1953 	return (0);
1954 }
1955 
1956 static int
1957 builtin_mapping(struct font *fp, int n)
1958 {
1959 	size_t size;
1960 	struct font_map *mp;
1961 
1962 	if (n >= VFNT_MAPS)
1963 		return (EINVAL);
1964 
1965 	if (fp->vf_map_count[n] == 0)
1966 		return (0);
1967 
1968 	size = fp->vf_map_count[n] * sizeof (*mp);
1969 	mp = malloc(size);
1970 	if (mp == NULL)
1971 		return (ENOMEM);
1972 	fp->vf_map[n] = mp;
1973 
1974 	memcpy(mp, DEFAULT_FONT_DATA.font->vf_map[n], size);
1975 	return (0);
1976 }
1977 
1978 /*
1979  * Load font from builtin or from file.
1980  * We do need special case for builtin because the builtin font glyphs
1981  * are compressed and we do need to uncompress them.
1982  * Having single load_font() for both cases will help us to simplify
1983  * font switch handling.
1984  */
1985 static bitmap_data_t *
1986 load_font(char *path)
1987 {
1988 	int fd, i;
1989 	uint32_t glyphs;
1990 	struct font_header fh;
1991 	struct fontlist *fl;
1992 	bitmap_data_t *bp;
1993 	struct font *fp;
1994 	size_t size;
1995 	ssize_t rv;
1996 
1997 	/* Get our entry from the font list. */
1998 	STAILQ_FOREACH(fl, &fonts, font_next) {
1999 		if (strcmp(fl->font_name, path) == 0)
2000 			break;
2001 	}
2002 	if (fl == NULL)
2003 		return (NULL);	/* Should not happen. */
2004 
2005 	bp = fl->font_data;
2006 	if (bp->font != NULL && fl->font_flags != FONT_RELOAD)
2007 		return (bp);
2008 
2009 	fd = -1;
2010 	/*
2011 	 * Special case for builtin font.
2012 	 * Builtin font is the very first font we load, we do not have
2013 	 * previous loads to be released.
2014 	 */
2015 	if (fl->font_flags == FONT_BUILTIN) {
2016 		if ((fp = calloc(1, sizeof (struct font))) == NULL)
2017 			return (NULL);
2018 
2019 		fp->vf_width = DEFAULT_FONT_DATA.width;
2020 		fp->vf_height = DEFAULT_FONT_DATA.height;
2021 
2022 		fp->vf_bytes = malloc(DEFAULT_FONT_DATA.uncompressed_size);
2023 		if (fp->vf_bytes == NULL) {
2024 			free(fp);
2025 			return (NULL);
2026 		}
2027 
2028 		bp->uncompressed_size = DEFAULT_FONT_DATA.uncompressed_size;
2029 		bp->compressed_size = DEFAULT_FONT_DATA.compressed_size;
2030 
2031 		if (lz4_decompress(DEFAULT_FONT_DATA.compressed_data,
2032 		    fp->vf_bytes,
2033 		    DEFAULT_FONT_DATA.compressed_size,
2034 		    DEFAULT_FONT_DATA.uncompressed_size, 0) != 0) {
2035 			free(fp->vf_bytes);
2036 			free(fp);
2037 			return (NULL);
2038 		}
2039 
2040 		for (i = 0; i < VFNT_MAPS; i++) {
2041 			fp->vf_map_count[i] =
2042 			    DEFAULT_FONT_DATA.font->vf_map_count[i];
2043 			if (builtin_mapping(fp, i) != 0)
2044 				goto free_done;
2045 		}
2046 
2047 		bp->font = fp;
2048 		return (bp);
2049 	}
2050 
2051 	fd = open(path, O_RDONLY);
2052 	if (fd < 0) {
2053 		return (NULL);
2054 	}
2055 
2056 	size = sizeof (fh);
2057 	rv = read(fd, &fh, size);
2058 	if (rv < 0 || (size_t)rv != size) {
2059 		bp = NULL;
2060 		goto done;
2061 	}
2062 	if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC, sizeof (fh.fh_magic)) != 0) {
2063 		bp = NULL;
2064 		goto done;
2065 	}
2066 	if ((fp = calloc(1, sizeof (struct font))) == NULL) {
2067 		bp = NULL;
2068 		goto done;
2069 	}
2070 	for (i = 0; i < VFNT_MAPS; i++)
2071 		fp->vf_map_count[i] = be32toh(fh.fh_map_count[i]);
2072 
2073 	glyphs = be32toh(fh.fh_glyph_count);
2074 	fp->vf_width = fh.fh_width;
2075 	fp->vf_height = fh.fh_height;
2076 
2077 	size = howmany(fp->vf_width, 8) * fp->vf_height * glyphs;
2078 	bp->uncompressed_size = size;
2079 	if ((fp->vf_bytes = malloc(size)) == NULL)
2080 		goto free_done;
2081 
2082 	rv = read(fd, fp->vf_bytes, size);
2083 	if (rv < 0 || (size_t)rv != size)
2084 		goto free_done;
2085 	for (i = 0; i < VFNT_MAPS; i++) {
2086 		if (load_mapping(fd, fp, i) != 0)
2087 			goto free_done;
2088 	}
2089 
2090 	/*
2091 	 * Reset builtin flag now as we have full font loaded.
2092 	 */
2093 	if (fl->font_flags == FONT_BUILTIN)
2094 		fl->font_flags = FONT_AUTO;
2095 
2096 	/*
2097 	 * Release previously loaded entries. We can do this now, as
2098 	 * the new font is loaded. Note, there can be no console
2099 	 * output till the new font is in place and tem is notified.
2100 	 * We do need to keep fl->font_data for glyph dimensions.
2101 	 */
2102 	STAILQ_FOREACH(fl, &fonts, font_next) {
2103 		if (fl->font_data->font == NULL)
2104 			continue;
2105 
2106 		for (i = 0; i < VFNT_MAPS; i++)
2107 			free(fl->font_data->font->vf_map[i]);
2108 		free(fl->font_data->font->vf_bytes);
2109 		free(fl->font_data->font);
2110 		fl->font_data->font = NULL;
2111 	}
2112 
2113 	bp->font = fp;
2114 	bp->compressed_size = 0;
2115 
2116 done:
2117 	if (fd != -1)
2118 		close(fd);
2119 	return (bp);
2120 
2121 free_done:
2122 	for (i = 0; i < VFNT_MAPS; i++)
2123 		free(fp->vf_map[i]);
2124 	free(fp->vf_bytes);
2125 	free(fp);
2126 	bp = NULL;
2127 	goto done;
2128 }
2129 
2130 
2131 struct name_entry {
2132 	char			*n_name;
2133 	SLIST_ENTRY(name_entry)	n_entry;
2134 };
2135 
2136 SLIST_HEAD(name_list, name_entry);
2137 
2138 /* Read font names from index file. */
2139 static struct name_list *
2140 read_list(char *fonts)
2141 {
2142 	struct name_list *nl;
2143 	struct name_entry *np;
2144 	char buf[PATH_MAX];
2145 	int fd, len;
2146 
2147 	fd = open(fonts, O_RDONLY);
2148 	if (fd < 0)
2149 		return (NULL);
2150 
2151 	nl = malloc(sizeof (*nl));
2152 	if (nl == NULL) {
2153 		close(fd);
2154 		return (nl);
2155 	}
2156 
2157 	SLIST_INIT(nl);
2158 	while ((len = fgetstr(buf, sizeof (buf), fd)) > 0) {
2159 		np = malloc(sizeof (*np));
2160 		if (np == NULL) {
2161 			close(fd);
2162 			return (nl);    /* return what we have */
2163 		}
2164 		np->n_name = strdup(buf);
2165 		if (np->n_name == NULL) {
2166 			free(np);
2167 			close(fd);
2168 			return (nl);    /* return what we have */
2169 		}
2170 		SLIST_INSERT_HEAD(nl, np, n_entry);
2171 	}
2172 	close(fd);
2173 	return (nl);
2174 }
2175 
2176 /*
2177  * Read the font properties and insert new entry into the list.
2178  * The font list is built in descending order.
2179  */
2180 static bool
2181 insert_font(char *name, FONT_FLAGS flags)
2182 {
2183 	struct font_header fh;
2184 	struct fontlist *fp, *previous, *entry, *next;
2185 	size_t size;
2186 	ssize_t rv;
2187 	int fd;
2188 	char *font_name;
2189 
2190 	font_name = NULL;
2191 	if (flags == FONT_BUILTIN) {
2192 		/*
2193 		 * We only install builtin font once, while setting up
2194 		 * initial console. Since this will happen very early,
2195 		 * we assume asprintf will not fail. Once we have access to
2196 		 * files, the builtin font will be replaced by font loaded
2197 		 * from file.
2198 		 */
2199 		if (!STAILQ_EMPTY(&fonts))
2200 			return (false);
2201 
2202 		fh.fh_width = DEFAULT_FONT_DATA.width;
2203 		fh.fh_height = DEFAULT_FONT_DATA.height;
2204 
2205 		(void) asprintf(&font_name, "%dx%d",
2206 		    DEFAULT_FONT_DATA.width, DEFAULT_FONT_DATA.height);
2207 	} else {
2208 		fd = open(name, O_RDONLY);
2209 		if (fd < 0)
2210 			return (false);
2211 		rv = read(fd, &fh, sizeof (fh));
2212 		close(fd);
2213 		if (rv < 0 || (size_t)rv != sizeof (fh))
2214 			return (false);
2215 
2216 		if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC,
2217 		    sizeof (fh.fh_magic)) != 0)
2218 			return (false);
2219 		font_name = strdup(name);
2220 	}
2221 
2222 	if (font_name == NULL)
2223 		return (false);
2224 
2225 	/*
2226 	 * If we have an entry with the same glyph dimensions, replace
2227 	 * the file name and mark us. We only support unique dimensions.
2228 	 */
2229 	STAILQ_FOREACH(entry, &fonts, font_next) {
2230 		if (fh.fh_width == entry->font_data->width &&
2231 		    fh.fh_height == entry->font_data->height) {
2232 			free(entry->font_name);
2233 			entry->font_name = font_name;
2234 			entry->font_flags = FONT_RELOAD;
2235 			return (true);
2236 		}
2237 	}
2238 
2239 	fp = calloc(sizeof (*fp), 1);
2240 	if (fp == NULL) {
2241 		free(font_name);
2242 		return (false);
2243 	}
2244 	fp->font_data = calloc(sizeof (*fp->font_data), 1);
2245 	if (fp->font_data == NULL) {
2246 		free(font_name);
2247 		free(fp);
2248 		return (false);
2249 	}
2250 	fp->font_name = font_name;
2251 	fp->font_flags = flags;
2252 	fp->font_load = load_font;
2253 	fp->font_data->width = fh.fh_width;
2254 	fp->font_data->height = fh.fh_height;
2255 
2256 	if (STAILQ_EMPTY(&fonts)) {
2257 		STAILQ_INSERT_HEAD(&fonts, fp, font_next);
2258 		return (true);
2259 	}
2260 
2261 	previous = NULL;
2262 	size = fp->font_data->width * fp->font_data->height;
2263 
2264 	STAILQ_FOREACH(entry, &fonts, font_next) {
2265 		/* Should fp be inserted before the entry? */
2266 		if (size > entry->font_data->width * entry->font_data->height) {
2267 			if (previous == NULL) {
2268 				STAILQ_INSERT_HEAD(&fonts, fp, font_next);
2269 			} else {
2270 				STAILQ_INSERT_AFTER(&fonts, previous, fp,
2271 				    font_next);
2272 			}
2273 			return (true);
2274 		}
2275 		next = STAILQ_NEXT(entry, font_next);
2276 		if (next == NULL ||
2277 		    size > next->font_data->width * next->font_data->height) {
2278 			STAILQ_INSERT_AFTER(&fonts, entry, fp, font_next);
2279 			return (true);
2280 		}
2281 		previous = entry;
2282 	}
2283 	return (true);
2284 }
2285 
2286 static int
2287 font_set(struct env_var *ev __unused, int flags __unused, const void *value)
2288 {
2289 	struct fontlist *fl;
2290 	char *eptr;
2291 	unsigned long x = 0, y = 0;
2292 
2293 	/*
2294 	 * Attempt to extract values from "XxY" string. In case of error,
2295 	 * we have unmaching glyph dimensions and will just output the
2296 	 * available values.
2297 	 */
2298 	if (value != NULL) {
2299 		x = strtoul(value, &eptr, 10);
2300 		if (*eptr == 'x')
2301 			y = strtoul(eptr + 1, &eptr, 10);
2302 	}
2303 	STAILQ_FOREACH(fl, &fonts, font_next) {
2304 		if (fl->font_data->width == x && fl->font_data->height == y)
2305 			break;
2306 	}
2307 	if (fl != NULL) {
2308 		/* Reset any FONT_MANUAL flag. */
2309 		reset_font_flags();
2310 
2311 		/* Mark this font manually loaded */
2312 		fl->font_flags = FONT_MANUAL;
2313 		/* Trigger tem update. */
2314 		tems.update_font = true;
2315 		plat_cons_update_mode(-1);
2316 		return (CMD_OK);
2317 	}
2318 
2319 	printf("Available fonts:\n");
2320 	STAILQ_FOREACH(fl, &fonts, font_next) {
2321 		printf("    %dx%d\n", fl->font_data->width,
2322 		    fl->font_data->height);
2323 	}
2324 	return (CMD_OK);
2325 }
2326 
2327 void
2328 bios_text_font(bool use_vga_font)
2329 {
2330 	if (use_vga_font)
2331 		(void) insert_font(VGA_8X16_FONT, FONT_MANUAL);
2332 	else
2333 		(void) insert_font(DEFAULT_8X16_FONT, FONT_MANUAL);
2334 	tems.update_font = true;
2335 }
2336 
2337 void
2338 autoload_font(bool bios)
2339 {
2340 	struct name_list *nl;
2341 	struct name_entry *np;
2342 
2343 	nl = read_list("/boot/fonts/fonts.dir");
2344 	if (nl == NULL)
2345 		return;
2346 
2347 	while (!SLIST_EMPTY(nl)) {
2348 		np = SLIST_FIRST(nl);
2349 		SLIST_REMOVE_HEAD(nl, n_entry);
2350 		if (insert_font(np->n_name, FONT_AUTO) == false)
2351 			printf("failed to add font: %s\n", np->n_name);
2352 		free(np->n_name);
2353 		free(np);
2354 	}
2355 
2356 	unsetenv("screen-font");
2357 	env_setenv("screen-font", EV_VOLATILE, NULL, font_set, env_nounset);
2358 
2359 	/*
2360 	 * If vga text mode was requested, load vga.font (8x16 bold) font.
2361 	 */
2362 	if (bios) {
2363 		bios_text_font(true);
2364 	}
2365 
2366 	/* Trigger tem update. */
2367 	tems.update_font = true;
2368 	plat_cons_update_mode(-1);
2369 }
2370 
2371 COMMAND_SET(load_font, "loadfont", "load console font from file", command_font);
2372 
2373 static int
2374 command_font(int argc, char *argv[])
2375 {
2376 	int i, c, rc = CMD_OK;
2377 	struct fontlist *fl;
2378 	bool list;
2379 
2380 	list = false;
2381 	optind = 1;
2382 	optreset = 1;
2383 	rc = CMD_OK;
2384 
2385 	while ((c = getopt(argc, argv, "l")) != -1) {
2386 		switch (c) {
2387 		case 'l':
2388 			list = true;
2389 			break;
2390 		case '?':
2391 		default:
2392 			return (CMD_ERROR);
2393 		}
2394 	}
2395 
2396 	argc -= optind;
2397 	argv += optind;
2398 
2399 	if (argc > 1 || (list && argc != 0)) {
2400 		printf("Usage: loadfont [-l] | [file.fnt]\n");
2401 		return (CMD_ERROR);
2402 	}
2403 
2404 	if (list) {
2405 		STAILQ_FOREACH(fl, &fonts, font_next) {
2406 			printf("font %s: %dx%d%s\n", fl->font_name,
2407 			    fl->font_data->width,
2408 			    fl->font_data->height,
2409 			    fl->font_data->font == NULL? "" : " loaded");
2410 		}
2411 		return (CMD_OK);
2412 	}
2413 
2414 	if (argc == 1) {
2415 		char *name = argv[0];
2416 
2417 		if (insert_font(name, FONT_MANUAL) == false) {
2418 			printf("loadfont error: failed to load: %s\n", name);
2419 			return (CMD_ERROR);
2420 		}
2421 
2422 		tems.update_font = true;
2423 		plat_cons_update_mode(-1);
2424 		return (CMD_OK);
2425 	}
2426 
2427 	if (argc == 0) {
2428 		/*
2429 		 * Walk entire font list, release any loaded font, and set
2430 		 * autoload flag. The font list does have at least the builtin
2431 		 * default font.
2432 		 */
2433 		STAILQ_FOREACH(fl, &fonts, font_next) {
2434 			if (fl->font_data->font != NULL) {
2435 				/* Note the tem is releasing font bytes */
2436 				for (i = 0; i < VFNT_MAPS; i++)
2437 					free(fl->font_data->font->vf_map[i]);
2438 				free(fl->font_data->font);
2439 				fl->font_data->font = NULL;
2440 				fl->font_data->uncompressed_size = 0;
2441 				fl->font_flags = FONT_AUTO;
2442 			}
2443 		}
2444 		tems.update_font = true;
2445 		plat_cons_update_mode(-1);
2446 	}
2447 	return (rc);
2448 }
2449 
2450 bool
2451 gfx_get_edid_resolution(struct vesa_edid_info *edid, edid_res_list_t *res)
2452 {
2453 	struct resolution *rp, *p;
2454 
2455 	/*
2456 	 * Walk detailed timings tables (4).
2457 	 */
2458 	if ((edid->display.supported_features
2459 	    & EDID_FEATURE_PREFERRED_TIMING_MODE) != 0) {
2460 		/* Walk detailed timing descriptors (4) */
2461 		for (int i = 0; i < DET_TIMINGS; i++) {
2462 			/*
2463 			 * Reserved value 0 is not used for display decriptor.
2464 			 */
2465 			if (edid->detailed_timings[i].pixel_clock == 0)
2466 				continue;
2467 			if ((rp = malloc(sizeof (*rp))) == NULL)
2468 				continue;
2469 			rp->width = GET_EDID_INFO_WIDTH(edid, i);
2470 			rp->height = GET_EDID_INFO_HEIGHT(edid, i);
2471 			if (rp->width > 0 && rp->width <= EDID_MAX_PIXELS &&
2472 			    rp->height > 0 && rp->height <= EDID_MAX_LINES)
2473 				TAILQ_INSERT_TAIL(res, rp, next);
2474 			else
2475 				free(rp);
2476 		}
2477 	}
2478 
2479 	/*
2480 	 * Walk standard timings list (8).
2481 	 */
2482 	for (int i = 0; i < STD_TIMINGS; i++) {
2483 		/* Is this field unused? */
2484 		if (edid->standard_timings[i] == 0x0101)
2485 			continue;
2486 
2487 		if ((rp = malloc(sizeof (*rp))) == NULL)
2488 			continue;
2489 
2490 		rp->width = HSIZE(edid->standard_timings[i]);
2491 		switch (RATIO(edid->standard_timings[i])) {
2492 		case RATIO1_1:
2493 			rp->height = HSIZE(edid->standard_timings[i]);
2494 			if (edid->header.version > 1 ||
2495 			    edid->header.revision > 2) {
2496 				rp->height = rp->height * 10 / 16;
2497 			}
2498 			break;
2499 		case RATIO4_3:
2500 			rp->height = HSIZE(edid->standard_timings[i]) * 3 / 4;
2501 			break;
2502 		case RATIO5_4:
2503 			rp->height = HSIZE(edid->standard_timings[i]) * 4 / 5;
2504 			break;
2505 		case RATIO16_9:
2506 			rp->height = HSIZE(edid->standard_timings[i]) * 9 / 16;
2507 			break;
2508 		}
2509 
2510 		/*
2511 		 * Create resolution list in decreasing order, except keep
2512 		 * first entry (preferred timing mode).
2513 		 */
2514 		TAILQ_FOREACH(p, res, next) {
2515 			if (p->width * p->height < rp->width * rp->height) {
2516 				/* Keep preferred mode first */
2517 				if (TAILQ_FIRST(res) == p)
2518 					TAILQ_INSERT_AFTER(res, p, rp, next);
2519 				else
2520 					TAILQ_INSERT_BEFORE(p, rp, next);
2521 				break;
2522 			}
2523 			if (TAILQ_NEXT(p, next) == NULL) {
2524 				TAILQ_INSERT_TAIL(res, rp, next);
2525 				break;
2526 			}
2527 		}
2528 	}
2529 	return (!TAILQ_EMPTY(res));
2530 }
2531