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 */
16
17/*
18 * Common functions to implement graphical framebuffer support for console.
19 */
20
21#include <sys/cdefs.h>
22#include <sys/param.h>
23#include <stand.h>
24#if	defined(EFI)
25#include <efi.h>
26#include <efilib.h>
27#else
28#include <btxv86.h>
29#endif
30#include <sys/tem_impl.h>
31#include <sys/consplat.h>
32#include <sys/visual_io.h>
33#include <sys/multiboot2.h>
34#include <sys/font.h>
35#include <sys/rgb.h>
36#include <sys/endian.h>
37#include <gfx_fb.h>
38#include <pnglite.h>
39#include <bootstrap.h>
40
41/*
42 * Global framebuffer struct, to be updated with mode changes.
43 */
44multiboot_tag_framebuffer_t gfx_fb;
45
46/* To support setenv, keep track of inverses and colors. */
47static int gfx_inverse = 0;
48static int gfx_inverse_screen = 0;
49static uint8_t gfx_fg = DEFAULT_ANSI_FOREGROUND;
50static uint8_t gfx_bg = DEFAULT_ANSI_BACKGROUND;
51
52static int gfx_fb_cons_clear(struct vis_consclear *);
53static void gfx_fb_cons_copy(struct vis_conscopy *);
54static void gfx_fb_cons_display(struct vis_consdisplay *);
55
56#if	defined(EFI)
57static int gfx_gop_cons_clear(uint32_t data, uint32_t width, uint32_t height);
58static void gfx_gop_cons_copy(struct vis_conscopy *);
59static void gfx_gop_cons_display(struct vis_consdisplay *);
60#endif
61static int gfx_bm_cons_clear(uint32_t data, uint32_t width, uint32_t height);
62static void gfx_bm_cons_copy(struct vis_conscopy *);
63static void gfx_bm_cons_display(struct vis_consdisplay *);
64
65/*
66 * Set default operations to use bitmap based implementation.
67 * In case of UEFI, if GOP is available, we will switch to GOP based
68 * implementation.
69 *
70 * Also note, for UEFI we do attempt to boost the execution by setting
71 * Task Priority Level (TPL) to TPL_NOTIFY, which is highest priority
72 * usable in application.
73 */
74struct gfx_fb_ops {
75	int (*gfx_cons_clear)(uint32_t, uint32_t, uint32_t);
76	void (*gfx_cons_copy)(struct vis_conscopy *);
77	void (*gfx_cons_display)(struct vis_consdisplay *);
78} gfx_fb_ops = {
79	.gfx_cons_clear = gfx_bm_cons_clear,
80	.gfx_cons_copy = gfx_bm_cons_copy,
81	.gfx_cons_display = gfx_bm_cons_display
82};
83
84/*
85 * Translate platform specific FB address.
86 */
87static uint8_t *
88gfx_get_fb_address(void)
89{
90#if	defined(EFI)
91	return ((uint8_t *)(uintptr_t)
92	    gfx_fb.framebuffer_common.framebuffer_addr);
93#else
94	return ((uint8_t *)PTOV((uint32_t)
95	    gfx_fb.framebuffer_common.framebuffer_addr & 0xffffffff));
96#endif
97}
98
99/*
100 * Generic platform callbacks for tem.
101 */
102void
103plat_tem_get_prom_font_size(int *charheight, int *windowtop)
104{
105	*charheight = 0;
106	*windowtop = 0;
107}
108
109void
110plat_tem_get_colors(uint8_t *fg, uint8_t *bg)
111{
112	*fg = gfx_fg;
113	*bg = gfx_bg;
114}
115
116void
117plat_tem_get_inverses(int *inverse, int *inverse_screen)
118{
119	*inverse = gfx_inverse;
120	*inverse_screen = gfx_inverse_screen;
121}
122
123/*
124 * Utility function to parse gfx mode line strings.
125 */
126bool
127gfx_parse_mode_str(char *str, int *x, int *y, int *depth)
128{
129	char *p, *end;
130
131	errno = 0;
132	p = str;
133	*x = strtoul(p, &end, 0);
134	if (*x == 0 || errno != 0)
135		return (false);
136	if (*end != 'x')
137		return (false);
138	p = end + 1;
139	*y = strtoul(p, &end, 0);
140	if (*y == 0 || errno != 0)
141		return (false);
142	if (*end != 'x') {
143		*depth = -1;    /* auto select */
144	} else {
145		p = end + 1;
146		*depth = strtoul(p, &end, 0);
147		if (*depth == 0 || errno != 0 || *end != '\0')
148			return (false);
149	}
150
151	return (true);
152}
153
154/*
155 * Support for color mapping.
156 */
157uint32_t
158gfx_fb_color_map(uint8_t index)
159{
160	rgb_t rgb;
161
162	if (gfx_fb.framebuffer_common.framebuffer_type !=
163	    MULTIBOOT_FRAMEBUFFER_TYPE_RGB) {
164		if (index < nitems(solaris_color_to_pc_color))
165			return (solaris_color_to_pc_color[index]);
166		else
167			return (index);
168	}
169
170	rgb.red.pos = gfx_fb.u.fb2.framebuffer_red_field_position;
171	rgb.red.size = gfx_fb.u.fb2.framebuffer_red_mask_size;
172
173	rgb.green.pos = gfx_fb.u.fb2.framebuffer_green_field_position;
174	rgb.green.size = gfx_fb.u.fb2.framebuffer_green_mask_size;
175
176	rgb.blue.pos = gfx_fb.u.fb2.framebuffer_blue_field_position;
177	rgb.blue.size = gfx_fb.u.fb2.framebuffer_blue_mask_size;
178
179	return (rgb_color_map(&rgb, index));
180}
181
182static bool
183color_name_to_ansi(const char *name, int *val)
184{
185	if (strcasecmp(name, "black") == 0) {
186		*val = ANSI_COLOR_BLACK;
187		return (true);
188	}
189	if (strcasecmp(name, "red") == 0) {
190		*val = ANSI_COLOR_RED;
191		return (true);
192	}
193	if (strcasecmp(name, "green") == 0) {
194		*val = ANSI_COLOR_GREEN;
195		return (true);
196	}
197	if (strcasecmp(name, "yellow") == 0) {
198		*val = ANSI_COLOR_YELLOW;
199		return (true);
200	}
201	if (strcasecmp(name, "blue") == 0) {
202		*val = ANSI_COLOR_BLUE;
203		return (true);
204	}
205	if (strcasecmp(name, "magenta") == 0) {
206		*val = ANSI_COLOR_MAGENTA;
207		return (true);
208	}
209	if (strcasecmp(name, "cyan") == 0) {
210		*val = ANSI_COLOR_CYAN;
211		return (true);
212	}
213	if (strcasecmp(name, "white") == 0) {
214		*val = ANSI_COLOR_WHITE;
215		return (true);
216	}
217	return (false);
218}
219
220/* Callback to check and set colors */
221static int
222gfx_set_colors(struct env_var *ev, int flags, const void *value)
223{
224	int val = 0, limit;
225	char buf[2];
226	const void *evalue;
227
228	if (value == NULL)
229		return (CMD_OK);
230
231	if (gfx_fb.framebuffer_common.framebuffer_bpp < 24)
232		limit = 7;
233	else
234		limit = 255;
235
236	if (color_name_to_ansi(value, &val)) {
237		snprintf(buf, sizeof (buf), "%d", val);
238		evalue = buf;
239	} else {
240		char *end;
241
242		errno = 0;
243		val = (int)strtol(value, &end, 0);
244		if (errno != 0 || *end != '\0') {
245			printf("Allowed values are either ansi color name or "
246			    "number from range [0-7]%s.\n",
247			    limit == 7 ? "" : " or [16-255]");
248			return (CMD_OK);
249		}
250		evalue = value;
251	}
252
253	/* invalid value? */
254	if ((val < 0 || val > limit) || (val > 7 && val < 16)) {
255		printf("Allowed values are either ansi color name or "
256		    "number from range [0-7]%s.\n",
257		    limit == 7 ? "" : " or [16-255]");
258		return (CMD_OK);
259	}
260
261	if (strcmp(ev->ev_name, "tem.fg_color") == 0) {
262		/* is it already set? */
263		if (gfx_fg == val)
264			return (CMD_OK);
265		gfx_fg = val;
266	}
267	if (strcmp(ev->ev_name, "tem.bg_color") == 0) {
268		/* is it already set? */
269		if (gfx_bg == val)
270			return (CMD_OK);
271		gfx_bg = val;
272	}
273	env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL);
274	plat_cons_update_mode(-1);
275	return (CMD_OK);
276}
277
278/* Callback to check and set inverses */
279static int
280gfx_set_inverses(struct env_var *ev, int flags, const void *value)
281{
282	int t, f;
283
284	if (value == NULL)
285		return (CMD_OK);
286
287	t = strcmp(value, "true");
288	f = strcmp(value, "false");
289
290	/* invalid value? */
291	if (t != 0 && f != 0)
292		return (CMD_OK);
293
294	if (strcmp(ev->ev_name, "tem.inverse") == 0) {
295		/* is it already set? */
296		if (gfx_inverse == (t == 0))
297			return (CMD_OK);
298		gfx_inverse = (t == 0);
299	}
300	if (strcmp(ev->ev_name, "tem.inverse-screen") == 0) {
301		/* is it already set? */
302		if (gfx_inverse_screen == (t == 0))
303			return (CMD_OK);
304		gfx_inverse_screen = (t == 0);
305	}
306	env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
307	plat_cons_update_mode(-1);
308	return (CMD_OK);
309}
310
311/*
312 * Initialize gfx framework.
313 */
314void
315gfx_framework_init(struct visual_ops *fb_ops)
316{
317	int rc, limit;
318	char *env, buf[2];
319#if	defined(EFI)
320	extern EFI_GRAPHICS_OUTPUT *gop;
321
322	if (gop != NULL) {
323		gfx_fb_ops.gfx_cons_clear = gfx_gop_cons_clear;
324		gfx_fb_ops.gfx_cons_copy = gfx_gop_cons_copy;
325		gfx_fb_ops.gfx_cons_display = gfx_gop_cons_display;
326	}
327#endif
328
329	if (gfx_fb.framebuffer_common.framebuffer_bpp < 24)
330		limit = 7;
331	else
332		limit = 255;
333
334	/* Add visual io callbacks */
335	fb_ops->cons_clear = gfx_fb_cons_clear;
336	fb_ops->cons_copy = gfx_fb_cons_copy;
337	fb_ops->cons_display = gfx_fb_cons_display;
338
339	/* set up tem inverse controls */
340	env = getenv("tem.inverse");
341	if (env != NULL) {
342		if (strcmp(env, "true") == 0)
343			gfx_inverse = 1;
344		unsetenv("tem.inverse");
345	}
346
347	env = getenv("tem.inverse-screen");
348	if (env != NULL) {
349		if (strcmp(env, "true") == 0)
350			gfx_inverse_screen = 1;
351		unsetenv("tem.inverse-screen");
352	}
353
354	if (gfx_inverse)
355		env = "true";
356	else
357		env = "false";
358
359	env_setenv("tem.inverse", EV_VOLATILE, env, gfx_set_inverses,
360	    env_nounset);
361
362	if (gfx_inverse_screen)
363		env = "true";
364	else
365		env = "false";
366
367	env_setenv("tem.inverse-screen", EV_VOLATILE, env, gfx_set_inverses,
368	    env_nounset);
369
370	/* set up tem color controls */
371	env = getenv("tem.fg_color");
372	if (env != NULL) {
373		rc = (int)strtol(env, NULL, 0);
374		if ((rc >= 0 && rc <= limit) && (rc <= 7 || rc >= 16))
375			gfx_fg = rc;
376		unsetenv("tem.fg_color");
377	}
378
379	env = getenv("tem.bg_color");
380	if (env != NULL) {
381		rc = (int)strtol(env, NULL, 0);
382		if ((rc >= 0 && rc <= limit) && (rc <= 7 || rc >= 16))
383			gfx_bg = rc;
384		unsetenv("tem.bg_color");
385	}
386
387	snprintf(buf, sizeof (buf), "%d", gfx_fg);
388	env_setenv("tem.fg_color", EV_VOLATILE, buf, gfx_set_colors,
389	    env_nounset);
390	snprintf(buf, sizeof (buf), "%d", gfx_bg);
391	env_setenv("tem.bg_color", EV_VOLATILE, buf, gfx_set_colors,
392	    env_nounset);
393}
394
395/*
396 * visual io callbacks.
397 */
398
399#if	defined(EFI)
400static int
401gfx_gop_cons_clear(uint32_t data, uint32_t width, uint32_t height)
402{
403	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer;
404	EFI_STATUS status;
405	extern EFI_GRAPHICS_OUTPUT *gop;
406
407	BltBuffer = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)&data;
408
409	status = gop->Blt(gop, BltBuffer, EfiBltVideoFill, 0, 0,
410	    0, 0, width, height, 0);
411
412	if (EFI_ERROR(status))
413		return (1);
414	else
415		return (0);
416}
417#endif
418
419static int
420gfx_bm_cons_clear(uint32_t data, uint32_t width, uint32_t height)
421{
422	uint8_t *fb, *fb8;
423	uint32_t *fb32, pitch;
424	uint16_t *fb16;
425	uint32_t i, j;
426
427	fb = gfx_get_fb_address();
428	pitch = gfx_fb.framebuffer_common.framebuffer_pitch;
429
430	switch (gfx_fb.framebuffer_common.framebuffer_bpp) {
431	case 8:		/* 8 bit */
432		for (i = 0; i < height; i++) {
433			(void) memset(fb + i * pitch, data, pitch);
434		}
435		break;
436	case 15:
437	case 16:		/* 16 bit */
438		for (i = 0; i < height; i++) {
439			fb16 = (uint16_t *)(fb + i * pitch);
440			for (j = 0; j < width; j++)
441				fb16[j] = (uint16_t)(data & 0xffff);
442		}
443		break;
444	case 24:		/* 24 bit */
445		for (i = 0; i < height; i++) {
446			fb8 = fb + i * pitch;
447			for (j = 0; j < pitch; j += 3) {
448				fb8[j] = (data >> 16) & 0xff;
449				fb8[j+1] = (data >> 8) & 0xff;
450				fb8[j+2] = data & 0xff;
451			}
452		}
453		break;
454	case 32:		/* 32 bit */
455		for (i = 0; i < height; i++) {
456			fb32 = (uint32_t *)(fb + i * pitch);
457			for (j = 0; j < width; j++)
458				fb32[j] = data;
459		}
460		break;
461	default:
462		return (1);
463	}
464
465	return (0);
466}
467
468static int
469gfx_fb_cons_clear(struct vis_consclear *ca)
470{
471	uint32_t data, width, height;
472	int ret;
473#if	defined(EFI)
474	EFI_TPL tpl;
475#endif
476
477	data = gfx_fb_color_map(ca->bg_color);
478	width = gfx_fb.framebuffer_common.framebuffer_width;
479	height = gfx_fb.framebuffer_common.framebuffer_height;
480
481#if	defined(EFI)
482	tpl = BS->RaiseTPL(TPL_NOTIFY);
483#endif
484	ret = gfx_fb_ops.gfx_cons_clear(data, width, height);
485#if	defined(EFI)
486	BS->RestoreTPL(tpl);
487#endif
488	return (ret);
489}
490
491#if	defined(EFI)
492static void
493gfx_gop_cons_copy(struct vis_conscopy *ma)
494{
495	UINTN width, height;
496	extern EFI_GRAPHICS_OUTPUT *gop;
497
498	width = ma->e_col - ma->s_col + 1;
499	height = ma->e_row - ma->s_row + 1;
500
501	(void) gop->Blt(gop, NULL, EfiBltVideoToVideo, ma->s_col, ma->s_row,
502	    ma->t_col, ma->t_row, width, height, 0);
503}
504#endif
505
506static void
507gfx_bm_cons_copy(struct vis_conscopy *ma)
508{
509	uint32_t soffset, toffset;
510	uint32_t width, height;
511	uint8_t *src, *dst, *fb;
512	uint32_t bpp, pitch;
513
514	fb = gfx_get_fb_address();
515	bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
516	pitch = gfx_fb.framebuffer_common.framebuffer_pitch;
517
518	soffset = ma->s_col * bpp + ma->s_row * pitch;
519	toffset = ma->t_col * bpp + ma->t_row * pitch;
520	src = fb + soffset;
521	dst = fb + toffset;
522	width = (ma->e_col - ma->s_col + 1) * bpp;
523	height = ma->e_row - ma->s_row + 1;
524
525	if (toffset <= soffset) {
526		for (uint32_t i = 0; i < height; i++) {
527			uint32_t increment = i * pitch;
528			(void) memmove(dst + increment, src + increment, width);
529		}
530	} else {
531		for (int i = height - 1; i >= 0; i--) {
532			uint32_t increment = i * pitch;
533			(void) memmove(dst + increment, src + increment, width);
534		}
535	}
536}
537
538static void
539gfx_fb_cons_copy(struct vis_conscopy *ma)
540{
541#if	defined(EFI)
542	EFI_TPL tpl;
543
544	tpl = BS->RaiseTPL(TPL_NOTIFY);
545#endif
546
547	gfx_fb_ops.gfx_cons_copy(ma);
548#if	defined(EFI)
549	BS->RestoreTPL(tpl);
550#endif
551}
552
553/*
554 * Implements alpha blending for RGBA data, could use pixels for arguments,
555 * but byte stream seems more generic.
556 * The generic alpha blending is:
557 * blend = alpha * fg + (1.0 - alpha) * bg.
558 * Since our alpha is not from range [0..1], we scale appropriately.
559 */
560static uint8_t
561alpha_blend(uint8_t fg, uint8_t bg, uint8_t alpha)
562{
563	uint16_t blend, h, l;
564
565	/* trivial corner cases */
566	if (alpha == 0)
567		return (bg);
568	if (alpha == 0xFF)
569		return (fg);
570	blend = (alpha * fg + (0xFF - alpha) * bg);
571	/* Division by 0xFF */
572	h = blend >> 8;
573	l = blend & 0xFF;
574	if (h + l >= 0xFF)
575		h++;
576	return (h);
577}
578
579/* Copy memory to framebuffer or to memory. */
580static void
581bitmap_cpy(uint8_t *dst, uint8_t *src, uint32_t len, int bpp)
582{
583	uint32_t i;
584	uint8_t a;
585
586	switch (bpp) {
587	case 4:
588		/*
589		 * we only implement alpha blending for depth 32,
590		 * use memcpy for other cases.
591		 */
592		for (i = 0; i < len; i += bpp) {
593			a = src[i+3];
594			dst[i] = alpha_blend(src[i], dst[i], a);
595			dst[i+1] = alpha_blend(src[i+1], dst[i+1], a);
596			dst[i+2] = alpha_blend(src[i+2], dst[i+2], a);
597			dst[i+3] = a;
598		}
599		break;
600	default:
601		(void) memcpy(dst, src, len);
602		break;
603	}
604}
605
606#if	defined(EFI)
607static void
608gfx_gop_cons_display(struct vis_consdisplay *da)
609{
610	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer;
611	uint32_t size;
612	int bpp;
613	extern EFI_GRAPHICS_OUTPUT *gop;
614
615	bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
616	size = sizeof (*BltBuffer) * da->width * da->height;
617	BltBuffer = malloc(size);
618	if (BltBuffer == NULL && gfx_get_fb_address() != NULL) {
619		/* Fall back to bitmap implementation */
620		gfx_bm_cons_display(da);
621		return;
622	}
623
624	(void) gop->Blt(gop, BltBuffer, EfiBltVideoToBltBuffer,
625	    da->col, da->row, 0, 0, da->width, da->height, 0);
626	bitmap_cpy((void *)BltBuffer, da->data, size, bpp);
627	(void) gop->Blt(gop, BltBuffer, EfiBltBufferToVideo,
628	    0, 0, da->col, da->row, da->width, da->height, 0);
629	free(BltBuffer);
630}
631#endif
632
633static void
634gfx_bm_cons_display(struct vis_consdisplay *da)
635{
636	uint32_t size;		/* write size per scanline */
637	uint8_t *fbp;		/* fb + calculated offset */
638	int i, bpp, pitch;
639
640	bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
641	pitch = gfx_fb.framebuffer_common.framebuffer_pitch;
642
643	size = da->width * bpp;
644	fbp = gfx_get_fb_address();
645	fbp += da->col * bpp + da->row * pitch;
646
647	/* write all scanlines in rectangle */
648	for (i = 0; i < da->height; i++) {
649		uint8_t *dest = fbp + i * pitch;
650		uint8_t *src = da->data + i * size;
651		bitmap_cpy(dest, src, size, bpp);
652	}
653}
654
655static void
656gfx_fb_cons_display(struct vis_consdisplay *da)
657{
658#if	defined(EFI)
659	EFI_TPL tpl;
660#endif
661
662	/* make sure we will not write past FB */
663	if ((uint32_t)da->col >= gfx_fb.framebuffer_common.framebuffer_width ||
664	    (uint32_t)da->row >= gfx_fb.framebuffer_common.framebuffer_height ||
665	    (uint32_t)da->col + da->width >
666	    gfx_fb.framebuffer_common.framebuffer_width ||
667	    (uint32_t)da->row + da->height >
668	    gfx_fb.framebuffer_common.framebuffer_height)
669		return;
670
671#if	defined(EFI)
672	tpl = BS->RaiseTPL(TPL_NOTIFY);
673#endif
674	gfx_fb_ops.gfx_cons_display(da);
675#if	defined(EFI)
676	BS->RestoreTPL(tpl);
677#endif
678}
679
680void
681gfx_fb_display_cursor(struct vis_conscursor *ca)
682{
683	uint32_t fg, bg;
684	uint32_t offset, size, *fb32;
685	uint16_t *fb16;
686	uint8_t *fb8, *fb;
687	uint32_t bpp, pitch;
688#if	defined(EFI)
689	EFI_TPL tpl;
690#endif
691
692	fb = gfx_get_fb_address();
693	bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
694	pitch = gfx_fb.framebuffer_common.framebuffer_pitch;
695
696	size = ca->width * bpp;
697
698	/*
699	 * Build cursor image. We are building mirror image of data on
700	 * frame buffer by (D xor FG) xor BG.
701	 */
702	offset = ca->col * bpp + ca->row * pitch;
703#if	defined(EFI)
704	tpl = BS->RaiseTPL(TPL_NOTIFY);
705#endif
706	switch (gfx_fb.framebuffer_common.framebuffer_bpp) {
707	case 8:		/* 8 bit */
708		fg = ca->fg_color.mono;
709		bg = ca->bg_color.mono;
710		for (int i = 0; i < ca->height; i++) {
711			fb8 = fb + offset + i * pitch;
712			for (uint32_t j = 0; j < size; j += 1) {
713				fb8[j] = (fb8[j] ^ (fg & 0xff)) ^ (bg & 0xff);
714			}
715		}
716		break;
717	case 15:
718	case 16:	/* 16 bit */
719		fg = ca->fg_color.sixteen[0] << 8;
720		fg |= ca->fg_color.sixteen[1];
721		bg = ca->bg_color.sixteen[0] << 8;
722		bg |= ca->bg_color.sixteen[1];
723		for (int i = 0; i < ca->height; i++) {
724			fb16 = (uint16_t *)(fb + offset + i * pitch);
725			for (int j = 0; j < ca->width; j++) {
726				fb16[j] = (fb16[j] ^ (fg & 0xffff)) ^
727				    (bg & 0xffff);
728			}
729		}
730		break;
731	case 24:	/* 24 bit */
732		fg = ca->fg_color.twentyfour[0] << 16;
733		fg |= ca->fg_color.twentyfour[1] << 8;
734		fg |= ca->fg_color.twentyfour[2];
735		bg = ca->bg_color.twentyfour[0] << 16;
736		bg |= ca->bg_color.twentyfour[1] << 8;
737		bg |= ca->bg_color.twentyfour[2];
738
739		for (int i = 0; i < ca->height; i++) {
740			fb8 = fb + offset + i * pitch;
741			for (uint32_t j = 0; j < size; j += 3) {
742				fb8[j] = (fb8[j] ^ ((fg >> 16) & 0xff)) ^
743				    ((bg >> 16) & 0xff);
744				fb8[j+1] = (fb8[j+1] ^ ((fg >> 8) & 0xff)) ^
745				    ((bg >> 8) & 0xff);
746				fb8[j+2] = (fb8[j+2] ^ (fg & 0xff)) ^
747				    (bg & 0xff);
748			}
749		}
750		break;
751	case 32:	/* 32 bit */
752		fg = ca->fg_color.twentyfour[0] << 16;
753		fg |= ca->fg_color.twentyfour[1] << 8;
754		fg |= ca->fg_color.twentyfour[2];
755		bg = ca->bg_color.twentyfour[0] << 16;
756		bg |= ca->bg_color.twentyfour[1] << 8;
757		bg |= ca->bg_color.twentyfour[2];
758		for (int i = 0; i < ca->height; i++) {
759			fb32 = (uint32_t *)(fb + offset + i * pitch);
760			for (int j = 0; j < ca->width; j++)
761				fb32[j] = (fb32[j] ^ fg) ^ bg;
762		}
763		break;
764	}
765#if	defined(EFI)
766	BS->RestoreTPL(tpl);
767#endif
768}
769
770/*
771 * Public graphics primitives.
772 */
773
774static int
775isqrt(int num)
776{
777	int res = 0;
778	int bit = 1 << 30;
779
780	/* "bit" starts at the highest power of four <= the argument. */
781	while (bit > num)
782		bit >>= 2;
783
784	while (bit != 0) {
785		if (num >= res + bit) {
786			num -= res + bit;
787			res = (res >> 1) + bit;
788		} else
789			res >>= 1;
790		bit >>= 2;
791	}
792	return (res);
793}
794
795/* set pixel in framebuffer using gfx coordinates */
796void
797gfx_fb_setpixel(uint32_t x, uint32_t y)
798{
799	uint32_t c, offset, pitch, bpp;
800	uint8_t *fb;
801	text_color_t fg, bg;
802
803	if (plat_stdout_is_framebuffer() == 0)
804		return;
805
806	tem_get_colors((tem_vt_state_t)tems.ts_active, &fg, &bg);
807	c = gfx_fb_color_map(fg);
808
809	if (x >= gfx_fb.framebuffer_common.framebuffer_width ||
810	    y >= gfx_fb.framebuffer_common.framebuffer_height)
811		return;
812
813	fb = gfx_get_fb_address();
814	pitch = gfx_fb.framebuffer_common.framebuffer_pitch;
815	bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
816
817	offset = y * pitch + x * bpp;
818	switch (gfx_fb.framebuffer_common.framebuffer_bpp) {
819	case 8:
820		fb[offset] = c & 0xff;
821		break;
822	case 15:
823	case 16:
824		*(uint16_t *)(fb + offset) = c & 0xffff;
825		break;
826	case 24:
827		fb[offset] = (c >> 16) & 0xff;
828		fb[offset + 1] = (c >> 8) & 0xff;
829		fb[offset + 2] = c & 0xff;
830		break;
831	case 32:
832		*(uint32_t *)(fb + offset) = c;
833		break;
834	}
835}
836
837/*
838 * draw rectangle in framebuffer using gfx coordinates.
839 * The function is borrowed from fbsd vt_fb.c
840 */
841void
842gfx_fb_drawrect(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2,
843    uint32_t fill)
844{
845	uint32_t x, y;
846
847	if (plat_stdout_is_framebuffer() == 0)
848		return;
849
850	for (y = y1; y <= y2; y++) {
851		if (fill || (y == y1) || (y == y2)) {
852			for (x = x1; x <= x2; x++)
853				gfx_fb_setpixel(x, y);
854		} else {
855			gfx_fb_setpixel(x1, y);
856			gfx_fb_setpixel(x2, y);
857		}
858	}
859}
860
861void
862gfx_fb_line(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t wd)
863{
864	int dx, sx, dy, sy;
865	int err, e2, x2, y2, ed, width;
866
867	if (plat_stdout_is_framebuffer() == 0)
868		return;
869
870	width = wd;
871	sx = x0 < x1? 1 : -1;
872	sy = y0 < y1? 1 : -1;
873	dx = x1 > x0? x1 - x0 : x0 - x1;
874	dy = y1 > y0? y1 - y0 : y0 - y1;
875	err = dx + dy;
876	ed = dx + dy == 0 ? 1: isqrt(dx * dx + dy * dy);
877
878	for (;;) {
879		gfx_fb_setpixel(x0, y0);
880		e2 = err;
881		x2 = x0;
882		if ((e2 << 1) >= -dx) {		/* x step */
883			e2 += dy;
884			y2 = y0;
885			while (e2 < ed * width &&
886			    (y1 != (uint32_t)y2 || dx > dy)) {
887				y2 += sy;
888				gfx_fb_setpixel(x0, y2);
889				e2 += dx;
890			}
891			if (x0 == x1)
892				break;
893			e2 = err;
894			err -= dy;
895			x0 += sx;
896		}
897		if ((e2 << 1) <= dy) {		/* y step */
898			e2 = dx-e2;
899			while (e2 < ed * width &&
900			    (x1 != (uint32_t)x2 || dx < dy)) {
901				x2 += sx;
902				gfx_fb_setpixel(x2, y0);
903				e2 += dy;
904			}
905			if (y0 == y1)
906				break;
907			err += dx;
908			y0 += sy;
909		}
910	}
911}
912
913/*
914 * quadratic B��zier curve limited to gradients without sign change.
915 */
916void
917gfx_fb_bezier(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t x2,
918    uint32_t y2, uint32_t wd)
919{
920	int sx, sy, xx, yy, xy, width;
921	int dx, dy, err, curvature;
922	int i;
923
924	if (plat_stdout_is_framebuffer() == 0)
925		return;
926
927	width = wd;
928	sx = x2 - x1;
929	sy = y2 - y1;
930	xx = x0 - x1;
931	yy = y0 - y1;
932	curvature = xx*sy - yy*sx;
933
934	if (sx*sx + sy*sy > xx*xx+yy*yy) {
935		x2 = x0;
936		x0 = sx + x1;
937		y2 = y0;
938		y0 = sy + y1;
939		curvature = -curvature;
940	}
941	if (curvature != 0) {
942		xx += sx;
943		sx = x0 < x2? 1 : -1;
944		xx *= sx;
945		yy += sy;
946		sy = y0 < y2? 1 : -1;
947		yy *= sy;
948		xy = (xx*yy) << 1;
949		xx *= xx;
950		yy *= yy;
951		if (curvature * sx * sy < 0) {
952			xx = -xx;
953			yy = -yy;
954			xy = -xy;
955			curvature = -curvature;
956		}
957		dx = 4 * sy * curvature * (x1 - x0) + xx - xy;
958		dy = 4 * sx * curvature * (y0 - y1) + yy - xy;
959		xx += xx;
960		yy += yy;
961		err = dx + dy + xy;
962		do {
963			for (i = 0; i <= width; i++)
964				gfx_fb_setpixel(x0 + i, y0);
965			if (x0 == x2 && y0 == y2)
966				return;  /* last pixel -> curve finished */
967			y1 = 2 * err < dx;
968			if (2 * err > dy) {
969				x0 += sx;
970				dx -= xy;
971				dy += yy;
972				err += dy;
973			}
974			if (y1 != 0) {
975				y0 += sy;
976				dy -= xy;
977				dx += xx;
978				err += dx;
979			}
980		} while (dy < dx); /* gradient negates -> algorithm fails */
981	}
982	gfx_fb_line(x0, y0, x2, y2, width);
983}
984
985/*
986 * draw rectangle using terminal coordinates and current foreground color.
987 */
988void
989gfx_term_drawrect(uint32_t ux1, uint32_t uy1, uint32_t ux2, uint32_t uy2)
990{
991	int x1, y1, x2, y2;
992	int xshift, yshift;
993	int width, i;
994	uint32_t vf_width, vf_height;
995
996	if (plat_stdout_is_framebuffer() == 0)
997		return;
998
999	vf_width = tems.ts_font.vf_width;
1000	vf_height = tems.ts_font.vf_height;
1001	width = vf_width / 4;			/* line width */
1002	xshift = (vf_width - width) / 2;
1003	yshift = (vf_height - width) / 2;
1004	/* Terminal coordinates start from (1,1) */
1005	ux1--;
1006	uy1--;
1007	ux2--;
1008	uy2--;
1009
1010	/* mark area used in tem */
1011	tem_image_display(tems.ts_active, uy1 - 1, ux1 - 1, uy2, ux2);
1012
1013	/*
1014	 * Draw horizontal lines width points thick, shifted from outer edge.
1015	 */
1016	x1 = (ux1 + 1) * vf_width + tems.ts_p_offset.x;
1017	y1 = uy1 * vf_height + tems.ts_p_offset.y + yshift;
1018	x2 = ux2 * vf_width + tems.ts_p_offset.x;
1019	gfx_fb_drawrect(x1, y1, x2, y1 + width, 1);
1020	y2 = uy2 * vf_height + tems.ts_p_offset.y;
1021	y2 += vf_height - yshift - width;
1022	gfx_fb_drawrect(x1, y2, x2, y2 + width, 1);
1023
1024	/*
1025	 * Draw vertical lines width points thick, shifted from outer edge.
1026	 */
1027	x1 = ux1 * vf_width + tems.ts_p_offset.x + xshift;
1028	y1 = uy1 * vf_height + tems.ts_p_offset.y;
1029	y1 += vf_height;
1030	y2 = uy2 * vf_height + tems.ts_p_offset.y;
1031	gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
1032	x1 = ux2 * vf_width + tems.ts_p_offset.x;
1033	x1 += vf_width - xshift - width;
1034	gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
1035
1036	/* Draw upper left corner. */
1037	x1 = ux1 * vf_width + tems.ts_p_offset.x + xshift;
1038	y1 = uy1 * vf_height + tems.ts_p_offset.y;
1039	y1 += vf_height;
1040
1041	x2 = ux1 * vf_width + tems.ts_p_offset.x;
1042	x2 += vf_width;
1043	y2 = uy1 * vf_height + tems.ts_p_offset.y + yshift;
1044	for (i = 0; i <= width; i++)
1045		gfx_fb_bezier(x1 + i, y1, x1 + i, y2 + i, x2, y2 + i, width-i);
1046
1047	/* Draw lower left corner. */
1048	x1 = ux1 * vf_width + tems.ts_p_offset.x;
1049	x1 += vf_width;
1050	y1 = uy2 * vf_height + tems.ts_p_offset.y;
1051	y1 += vf_height - yshift;
1052	x2 = ux1 * vf_width + tems.ts_p_offset.x + xshift;
1053	y2 = uy2 * vf_height + tems.ts_p_offset.y;
1054	for (i = 0; i <= width; i++)
1055		gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
1056
1057	/* Draw upper right corner. */
1058	x1 = ux2 * vf_width + tems.ts_p_offset.x;
1059	y1 = uy1 * vf_height + tems.ts_p_offset.y + yshift;
1060	x2 = ux2 * vf_width + tems.ts_p_offset.x;
1061	x2 += vf_width - xshift - width;
1062	y2 = uy1 * vf_height + tems.ts_p_offset.y;
1063	y2 += vf_height;
1064	for (i = 0; i <= width; i++)
1065		gfx_fb_bezier(x1, y1 + i, x2 + i, y1 + i, x2 + i, y2, width-i);
1066
1067	/* Draw lower right corner. */
1068	x1 = ux2 * vf_width + tems.ts_p_offset.x;
1069	y1 = uy2 * vf_height + tems.ts_p_offset.y;
1070	y1 += vf_height - yshift;
1071	x2 = ux2 * vf_width + tems.ts_p_offset.x;
1072	x2 += vf_width - xshift - width;
1073	y2 = uy2 * vf_height + tems.ts_p_offset.y;
1074	for (i = 0; i <= width; i++)
1075		gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
1076}
1077
1078#define	FL_PUTIMAGE_BORDER	0x1
1079#define	FL_PUTIMAGE_NOSCROLL	0x2
1080#define	FL_PUTIMAGE_DEBUG	0x80
1081
1082int
1083gfx_fb_putimage(png_t *png, uint32_t ux1, uint32_t uy1, uint32_t ux2,
1084    uint32_t uy2, uint32_t flags)
1085{
1086	struct vis_consdisplay da;
1087	uint32_t i, j, x, y, fheight, fwidth, color;
1088	int fbpp;
1089	uint8_t r, g, b, a, *p;
1090	bool scale = false;
1091	bool trace = false;
1092
1093	trace = (flags & FL_PUTIMAGE_DEBUG) != 0;
1094
1095	if (plat_stdout_is_framebuffer() == 0) {
1096		if (trace)
1097			printf("Framebuffer not active.\n");
1098		return (1);
1099	}
1100
1101	if (png->color_type != PNG_TRUECOLOR_ALPHA) {
1102		if (trace)
1103			printf("Not truecolor image.\n");
1104		return (1);
1105	}
1106
1107	if (ux1 > gfx_fb.framebuffer_common.framebuffer_width ||
1108	    uy1 > gfx_fb.framebuffer_common.framebuffer_height) {
1109		if (trace)
1110			printf("Top left coordinate off screen.\n");
1111		return (1);
1112	}
1113
1114	if (png->width > UINT16_MAX || png->height > UINT16_MAX) {
1115		if (trace)
1116			printf("Image too large.\n");
1117		return (1);
1118	}
1119
1120	if (png->width < 1 || png->height < 1) {
1121		if (trace)
1122			printf("Image too small.\n");
1123		return (1);
1124	}
1125
1126	/*
1127	 * If 0 was passed for either ux2 or uy2, then calculate the missing
1128	 * part of the bottom right coordinate.
1129	 */
1130	scale = true;
1131	if (ux2 == 0 && uy2 == 0) {
1132		/* Both 0, use the native resolution of the image */
1133		ux2 = ux1 + png->width;
1134		uy2 = uy1 + png->height;
1135		scale = false;
1136	} else if (ux2 == 0) {
1137		/* Set ux2 from uy2/uy1 to maintain aspect ratio */
1138		ux2 = ux1 + (png->width * (uy2 - uy1)) / png->height;
1139	} else if (uy2 == 0) {
1140		/* Set uy2 from ux2/ux1 to maintain aspect ratio */
1141		uy2 = uy1 + (png->height * (ux2 - ux1)) / png->width;
1142	}
1143
1144	if (ux2 > gfx_fb.framebuffer_common.framebuffer_width ||
1145	    uy2 > gfx_fb.framebuffer_common.framebuffer_height) {
1146		if (trace)
1147			printf("Bottom right coordinate off screen.\n");
1148		return (1);
1149	}
1150
1151	fwidth = ux2 - ux1;
1152	fheight = uy2 - uy1;
1153
1154	/*
1155	 * If the original image dimensions have been passed explicitly,
1156	 * disable scaling.
1157	 */
1158	if (fwidth == png->width && fheight == png->height)
1159		scale = false;
1160
1161	if (ux1 == 0) {
1162		/*
1163		 * No top left X co-ordinate (real coordinates start at 1),
1164		 * place as far right as it will fit.
1165		 */
1166		ux2 = gfx_fb.framebuffer_common.framebuffer_width -
1167		    tems.ts_p_offset.x;
1168		ux1 = ux2 - fwidth;
1169	}
1170
1171	if (uy1 == 0) {
1172		/*
1173		 * No top left Y co-ordinate (real coordinates start at 1),
1174		 * place as far down as it will fit.
1175		 */
1176		uy2 = gfx_fb.framebuffer_common.framebuffer_height -
1177		    tems.ts_p_offset.y;
1178		uy1 = uy2 - fheight;
1179	}
1180
1181	if (ux1 >= ux2 || uy1 >= uy2) {
1182		if (trace)
1183			printf("Image dimensions reversed.\n");
1184		return (1);
1185	}
1186
1187	if (fwidth < 2 || fheight < 2) {
1188		if (trace)
1189			printf("Target area too small\n");
1190		return (1);
1191	}
1192
1193	if (trace)
1194		printf("Image %ux%u -> %ux%u @%ux%u\n",
1195		    png->width, png->height, fwidth, fheight, ux1, uy1);
1196
1197	da.col = ux1;
1198	da.row = uy1;
1199	da.width = fwidth;
1200	da.height = fheight;
1201
1202	/*
1203	 * mark area used in tem
1204	 */
1205	if (!(flags & FL_PUTIMAGE_NOSCROLL)) {
1206		tem_image_display(tems.ts_active,
1207		    da.row / tems.ts_font.vf_height - 1,
1208		    da.col / tems.ts_font.vf_width - 1,
1209		    (da.row + da.height) / tems.ts_font.vf_height - 1,
1210		    (da.col + da.width) / tems.ts_font.vf_width - 1);
1211	}
1212
1213	if ((flags & FL_PUTIMAGE_BORDER))
1214		gfx_fb_drawrect(ux1, uy1, ux2, uy2, 0);
1215
1216	fbpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
1217
1218	da.data = malloc(fwidth * fheight * fbpp);
1219	if (da.data == NULL) {
1220		if (trace)
1221			printf("Out of memory.\n");
1222		return (1);
1223	}
1224
1225	/*
1226	 * Build image for our framebuffer.
1227	 */
1228
1229	/* Helper to calculate the pixel index from the source png */
1230#define	GETPIXEL(xx, yy) (((yy) * png->width + (xx)) * png->bpp)
1231
1232	/*
1233	 * For each of the x and y directions, calculate the number of pixels
1234	 * in the source image that correspond to a single pixel in the target.
1235	 * Use fixed-point arithmetic with 16-bits for each of the integer and
1236	 * fractional parts.
1237	 */
1238	const uint32_t wcstep = ((png->width - 1) << 16) / (fwidth - 1);
1239	const uint32_t hcstep = ((png->height - 1) << 16) / (fheight - 1);
1240
1241	uint32_t hc = 0;
1242	for (y = 0; y < fheight; y++) {
1243		uint32_t hc2 = (hc >> 9) & 0x7f;
1244		uint32_t hc1 = 0x80 - hc2;
1245
1246		uint32_t offset_y = hc >> 16;
1247		uint32_t offset_y1 = offset_y + 1;
1248
1249		uint32_t wc = 0;
1250		for (x = 0; x < fwidth; x++) {
1251			uint32_t wc2 = (wc >> 9) & 0x7f;
1252			uint32_t wc1 = 0x80 - wc2;
1253
1254			uint32_t offset_x = wc >> 16;
1255			uint32_t offset_x1 = offset_x + 1;
1256
1257			/* Target pixel index */
1258			j = (y * fwidth + x) * fbpp;
1259
1260			if (!scale) {
1261				i = GETPIXEL(x, y);
1262				r = png->image[i];
1263				g = png->image[i + 1];
1264				b = png->image[i + 2];
1265				a = png->image[i + 3];
1266			} else {
1267				uint8_t pixel[4];
1268
1269				uint32_t p00 = GETPIXEL(offset_x, offset_y);
1270				uint32_t p01 = GETPIXEL(offset_x, offset_y1);
1271				uint32_t p10 = GETPIXEL(offset_x1, offset_y);
1272				uint32_t p11 = GETPIXEL(offset_x1, offset_y1);
1273
1274				/*
1275				 * Given a 2x2 array of pixels in the source
1276				 * image, combine them to produce a single
1277				 * value for the pixel in the target image.
1278				 * Each column of pixels is combined using
1279				 * a weighted average where the top and bottom
1280				 * pixels contribute hc1 and hc2 respectively.
1281				 * The calculation for bottom pixel pB and
1282				 * top pixel pT is:
1283				 *   (pT * hc1 + pB * hc2) / (hc1 + hc2)
1284				 * Once the values are determined for the two
1285				 * columns of pixels, then the columns are
1286				 * averaged together in the same way but using
1287				 * wc1 and wc2 for the weightings.
1288				 *
1289				 * Since hc1 and hc2 are chosen so that
1290				 * hc1 + hc2 == 128 (and same for wc1 + wc2),
1291				 * the >> 14 below is a quick way to divide by
1292				 * (hc1 + hc2) * (wc1 + wc2)
1293				 */
1294				for (i = 0; i < 4; i++)
1295					pixel[i] = (
1296					    (png->image[p00 + i] * hc1 +
1297					    png->image[p01 + i] * hc2) * wc1 +
1298					    (png->image[p10 + i] * hc1 +
1299					    png->image[p11 + i] * hc2) * wc2)
1300					    >> 14;
1301
1302				r = pixel[0];
1303				g = pixel[1];
1304				b = pixel[2];
1305				a = pixel[3];
1306			}
1307
1308			color =
1309			    r >> (8 - gfx_fb.u.fb2.framebuffer_red_mask_size)
1310			    << gfx_fb.u.fb2.framebuffer_red_field_position |
1311			    g >> (8 - gfx_fb.u.fb2.framebuffer_green_mask_size)
1312			    << gfx_fb.u.fb2.framebuffer_green_field_position |
1313			    b >> (8 - gfx_fb.u.fb2.framebuffer_blue_mask_size)
1314			    << gfx_fb.u.fb2.framebuffer_blue_field_position;
1315
1316			switch (gfx_fb.framebuffer_common.framebuffer_bpp) {
1317			case 8: {
1318				uint32_t best, dist, k;
1319				int diff;
1320
1321				color = 0;
1322				best = 256 * 256 * 256;
1323				for (k = 0; k < 16; k++) {
1324					diff = r - cmap4_to_24.red[k];
1325					dist = diff * diff;
1326					diff = g - cmap4_to_24.green[k];
1327					dist += diff * diff;
1328					diff = b - cmap4_to_24.blue[k];
1329					dist += diff * diff;
1330
1331					if (dist < best) {
1332						color = k;
1333						best = dist;
1334						if (dist == 0)
1335							break;
1336					}
1337				}
1338				da.data[j] = solaris_color_to_pc_color[color];
1339				break;
1340			}
1341			case 15:
1342			case 16:
1343				*(uint16_t *)(da.data+j) = color;
1344				break;
1345			case 24:
1346				p = (uint8_t *)&color;
1347				da.data[j] = p[0];
1348				da.data[j+1] = p[1];
1349				da.data[j+2] = p[2];
1350				break;
1351			case 32:
1352				color |= a << 24;
1353				*(uint32_t *)(da.data+j) = color;
1354				break;
1355			}
1356			wc += wcstep;
1357		}
1358		hc += hcstep;
1359	}
1360
1361	gfx_fb_cons_display(&da);
1362	free(da.data);
1363	return (0);
1364}
1365
1366static int
1367load_mapping(int fd, struct font *fp, int n)
1368{
1369	size_t i, size;
1370	ssize_t rv;
1371	struct font_map *mp;
1372
1373	if (fp->vf_map_count[n] == 0)
1374		return (0);
1375
1376	size = fp->vf_map_count[n] * sizeof (*mp);
1377	mp = malloc(size);
1378	if (mp == NULL)
1379		return (ENOMEM);
1380	fp->vf_map[n] = mp;
1381
1382	rv = read(fd, mp, size);
1383	if (rv < 0 || (size_t)rv != size) {
1384		free(fp->vf_map[n]);
1385		fp->vf_map[n] = NULL;
1386		return (EIO);
1387	}
1388
1389	for (i = 0; i < fp->vf_map_count[n]; i++) {
1390		mp[i].font_src = be32toh(mp[i].font_src);
1391		mp[i].font_dst = be16toh(mp[i].font_dst);
1392		mp[i].font_len = be16toh(mp[i].font_len);
1393	}
1394	return (0);
1395}
1396
1397/* Load font from file. */
1398static bitmap_data_t *
1399load_font(char *path)
1400{
1401	int fd, i;
1402	uint32_t glyphs;
1403	struct font_header fh;
1404	struct fontlist *fl;
1405	bitmap_data_t *bp = NULL;
1406	struct font *fp;
1407	size_t size;
1408	ssize_t rv;
1409
1410	/* Get our entry from the font list. */
1411	STAILQ_FOREACH(fl, &fonts, font_next) {
1412		if (strcmp(fl->font_name, path) == 0)
1413			break;
1414	}
1415	if (fl == NULL)
1416		return (NULL);	/* Should not happen. */
1417	bp = fl->font_data;
1418	if (bp->font != NULL)
1419		return (bp);
1420
1421	fd = open(path, O_RDONLY);
1422	if (fd < 0) {
1423		return (NULL);
1424	}
1425
1426	size = sizeof (fh);
1427	rv = read(fd, &fh, size);
1428	if (rv < 0 || (size_t)rv != size) {
1429		bp = NULL;
1430		goto done;
1431	}
1432	if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC, sizeof (fh.fh_magic)) != 0) {
1433		bp = NULL;
1434		goto done;
1435	}
1436	if ((fp = calloc(1, sizeof (struct font))) == NULL) {
1437		bp = NULL;
1438		goto done;
1439	}
1440	for (i = 0; i < VFNT_MAPS; i++)
1441		fp->vf_map_count[i] = be32toh(fh.fh_map_count[i]);
1442
1443	glyphs = be32toh(fh.fh_glyph_count);
1444	fp->vf_width = fh.fh_width;
1445	fp->vf_height = fh.fh_height;
1446
1447	bp->uncompressed_size = howmany(bp->width, 8) * bp->height * glyphs;
1448	size = bp->uncompressed_size;
1449	if ((fp->vf_bytes = malloc(size)) == NULL)
1450		goto free_done;
1451
1452	rv = read(fd, fp->vf_bytes, size);
1453	if (rv < 0 || (size_t)rv != size)
1454		goto free_done;
1455	for (i = 0; i < VFNT_MAPS; i++) {
1456		if (load_mapping(fd, fp, i) != 0)
1457			goto free_done;
1458	}
1459	bp->font = fp;
1460
1461	/*
1462	 * Release previously loaded entry. We can do this now, as
1463	 * the new font is loaded. Note, there can be no console
1464	 * output till the new font is in place and tem is notified.
1465	 * We do need to keep fl->font_data for glyph dimensions.
1466	 */
1467	STAILQ_FOREACH(fl, &fonts, font_next) {
1468		if (fl->font_data->width == bp->width &&
1469		    fl->font_data->height == bp->height)
1470			continue;
1471
1472		if (fl->font_data->font != NULL) {
1473			for (i = 0; i < VFNT_MAPS; i++)
1474				free(fl->font_data->font->vf_map[i]);
1475
1476			/* Unset vf_bytes pointer in tem. */
1477			if (tems.ts_font.vf_bytes ==
1478			    fl->font_data->font->vf_bytes) {
1479				tems.ts_font.vf_bytes = NULL;
1480			}
1481			free(fl->font_data->font->vf_bytes);
1482			free(fl->font_data->font);
1483			fl->font_data->font = NULL;
1484			fl->font_data->uncompressed_size = 0;
1485			fl->font_flags = FONT_AUTO;
1486		}
1487	}
1488
1489	/* free the uncompressed builtin font data in tem. */
1490	free(tems.ts_font.vf_bytes);
1491	tems.ts_font.vf_bytes = NULL;
1492
1493done:
1494	close(fd);
1495	return (bp);
1496
1497free_done:
1498	for (i = 0; i < VFNT_MAPS; i++)
1499		free(fp->vf_map[i]);
1500	free(fp->vf_bytes);
1501	free(fp);
1502	bp = NULL;
1503	goto done;
1504}
1505
1506
1507struct name_entry {
1508	char			*n_name;
1509	SLIST_ENTRY(name_entry)	n_entry;
1510};
1511
1512SLIST_HEAD(name_list, name_entry);
1513
1514/* Read font names from index file. */
1515static struct name_list *
1516read_list(char *fonts)
1517{
1518	struct name_list *nl;
1519	struct name_entry *np;
1520	char buf[PATH_MAX];
1521	int fd, len;
1522
1523	fd = open(fonts, O_RDONLY);
1524	if (fd < 0)
1525		return (NULL);
1526
1527	nl = malloc(sizeof (*nl));
1528	if (nl == NULL) {
1529		close(fd);
1530		return (nl);
1531	}
1532
1533	SLIST_INIT(nl);
1534	while ((len = fgetstr(buf, sizeof (buf), fd)) > 0) {
1535		np = malloc(sizeof (*np));
1536		if (np == NULL) {
1537			close(fd);
1538			return (nl);    /* return what we have */
1539		}
1540		np->n_name = strdup(buf);
1541		if (np->n_name == NULL) {
1542			free(np);
1543			close(fd);
1544			return (nl);    /* return what we have */
1545		}
1546		SLIST_INSERT_HEAD(nl, np, n_entry);
1547	}
1548	close(fd);
1549	return (nl);
1550}
1551
1552/*
1553 * Read the font properties and insert new entry into the list.
1554 * The font list is built in descending order.
1555 */
1556static bool
1557insert_font(char *name)
1558{
1559	struct font_header fh;
1560	struct fontlist *fp, *previous, *entry, *next;
1561	size_t size;
1562	ssize_t rv;
1563	int fd;
1564	char *font_name;
1565
1566	fd = open(name, O_RDONLY);
1567	if (fd < 0)
1568		return (false);
1569	rv = read(fd, &fh, sizeof (fh));
1570	close(fd);
1571	if (rv < 0 || (size_t)rv != sizeof (fh))
1572		return (false);
1573
1574	if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC, sizeof (fh.fh_magic)) != 0)
1575		return (false);
1576
1577	font_name = strdup(name);
1578	if (font_name == NULL)
1579		return (false);
1580
1581	/*
1582	 * If we have an entry with the same glyph dimensions, just replace
1583	 * the file name. We only support unique dimensions.
1584	 */
1585	STAILQ_FOREACH(entry, &fonts, font_next) {
1586		if (fh.fh_width == entry->font_data->width &&
1587		    fh.fh_height == entry->font_data->height) {
1588			free(entry->font_name);
1589			entry->font_name = font_name;
1590			return (true);
1591		}
1592	}
1593
1594	fp = calloc(sizeof (*fp), 1);
1595	if (fp == NULL) {
1596		free(font_name);
1597		return (false);
1598	}
1599	fp->font_data = calloc(sizeof (*fp->font_data), 1);
1600	if (fp->font_data == NULL) {
1601		free(font_name);
1602		free(fp);
1603		return (false);
1604	}
1605	fp->font_name = font_name;
1606	fp->font_flags = FONT_AUTO;
1607	fp->font_load = load_font;
1608	fp->font_data->width = fh.fh_width;
1609	fp->font_data->height = fh.fh_height;
1610
1611	if (STAILQ_EMPTY(&fonts)) {
1612		STAILQ_INSERT_HEAD(&fonts, fp, font_next);
1613		return (true);
1614	}
1615
1616	previous = NULL;
1617	size = fp->font_data->width * fp->font_data->height;
1618
1619	STAILQ_FOREACH(entry, &fonts, font_next) {
1620		/* Should fp be inserted before the entry? */
1621		if (size >
1622		    entry->font_data->width * entry->font_data->height) {
1623			if (previous == NULL) {
1624				STAILQ_INSERT_HEAD(&fonts, fp, font_next);
1625			} else {
1626				STAILQ_INSERT_AFTER(&fonts, previous, fp,
1627				    font_next);
1628			}
1629			return (true);
1630		}
1631		next = STAILQ_NEXT(entry, font_next);
1632		if (next == NULL ||
1633		    size > next->font_data->width * next->font_data->height) {
1634			STAILQ_INSERT_AFTER(&fonts, entry, fp, font_next);
1635			return (true);
1636		}
1637		previous = entry;
1638	}
1639	return (true);
1640}
1641
1642static int
1643font_set(struct env_var *ev __unused, int flags __unused, const void *value)
1644{
1645	struct fontlist *fl;
1646	char *eptr;
1647	unsigned long x = 0, y = 0;
1648
1649	/*
1650	 * Attempt to extract values from "XxY" string. In case of error,
1651	 * we have unmaching glyph dimensions and will just output the
1652	 * available values.
1653	 */
1654	if (value != NULL) {
1655		x = strtoul(value, &eptr, 10);
1656		if (*eptr == 'x')
1657			y = strtoul(eptr + 1, &eptr, 10);
1658	}
1659	STAILQ_FOREACH(fl, &fonts, font_next) {
1660		if (fl->font_data->width == x && fl->font_data->height == y)
1661			break;
1662	}
1663	if (fl != NULL) {
1664		/* Reset any FONT_MANUAL flag. */
1665		reset_font_flags();
1666
1667		/* Mark this font manually loaded */
1668		fl->font_flags = FONT_MANUAL;
1669		/* Trigger tem update. */
1670		tems.update_font = true;
1671		plat_cons_update_mode(-1);
1672		return (CMD_OK);
1673	}
1674
1675	printf("Available fonts:\n");
1676	STAILQ_FOREACH(fl, &fonts, font_next) {
1677		printf("    %dx%d\n", fl->font_data->width,
1678		    fl->font_data->height);
1679	}
1680	return (CMD_OK);
1681}
1682
1683void
1684autoload_font(void)
1685{
1686	struct name_list *nl;
1687	struct name_entry *np;
1688
1689	nl = read_list("/boot/fonts/fonts.dir");
1690	if (nl == NULL)
1691		return;
1692
1693	while (!SLIST_EMPTY(nl)) {
1694		np = SLIST_FIRST(nl);
1695		SLIST_REMOVE_HEAD(nl, n_entry);
1696		if (insert_font(np->n_name) == false)
1697			printf("failed to add font: %s\n", np->n_name);
1698		free(np->n_name);
1699		free(np);
1700	}
1701
1702	unsetenv("screen-font");
1703	env_setenv("screen-font", EV_VOLATILE, NULL, font_set, env_nounset);
1704	/* Trigger tem update. */
1705	tems.update_font = true;
1706	plat_cons_update_mode(-1);
1707}
1708
1709COMMAND_SET(load_font, "loadfont", "load console font from file", command_font);
1710
1711static int
1712command_font(int argc, char *argv[])
1713{
1714	int i, rc = CMD_OK;
1715	struct fontlist *fl;
1716	bitmap_data_t *bd;
1717
1718	if (argc > 2) {
1719		printf("Usage: loadfont [file.fnt]\n");
1720		return (CMD_ERROR);
1721	}
1722
1723	if (argc == 2) {
1724		char *name = argv[1];
1725
1726		if (insert_font(name) == false) {
1727			printf("loadfont error: failed to load: %s\n", name);
1728			return (CMD_ERROR);
1729		}
1730
1731		bd = load_font(name);
1732		if (bd == NULL) {
1733			printf("loadfont error: failed to load: %s\n", name);
1734			return (CMD_ERROR);
1735		}
1736
1737		/* Get the font list entry and mark it manually loaded. */
1738		STAILQ_FOREACH(fl, &fonts, font_next) {
1739			if (strcmp(fl->font_name, name) == 0)
1740				fl->font_flags = FONT_MANUAL;
1741		}
1742		tems.update_font = true;
1743		plat_cons_update_mode(-1);
1744		return (CMD_OK);
1745	}
1746
1747	if (argc == 1) {
1748		/*
1749		 * Walk entire font list, release any loaded font, and set
1750		 * autoload flag. If the font list is empty, the tem will
1751		 * get the builtin default.
1752		 */
1753		STAILQ_FOREACH(fl, &fonts, font_next) {
1754			if (fl->font_data->font != NULL) {
1755				/* Note the tem is releasing font bytes */
1756				for (i = 0; i < VFNT_MAPS; i++)
1757					free(fl->font_data->font->vf_map[i]);
1758				free(fl->font_data->font);
1759				fl->font_data->font = NULL;
1760				fl->font_data->uncompressed_size = 0;
1761				fl->font_flags = FONT_AUTO;
1762			}
1763		}
1764		tems.update_font = true;
1765		plat_cons_update_mode(-1);
1766	}
1767	return (rc);
1768}
1769