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