xref: /illumos-gate/usr/src/boot/sys/boot/common/gfx_fb.c (revision 190f051b)
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 *
gfx_get_fb_address(void)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
plat_tem_get_prom_font_size(int * charheight,int * windowtop)155 plat_tem_get_prom_font_size(int *charheight, int *windowtop)
156 {
157 	*charheight = 0;
158 	*windowtop = 0;
159 }
160 
161 void
plat_tem_get_colors(uint8_t * fg,uint8_t * bg)162 plat_tem_get_colors(uint8_t *fg, uint8_t *bg)
163 {
164 	*fg = gfx_fg;
165 	*bg = gfx_bg;
166 }
167 
168 void
plat_tem_get_inverses(int * inverse,int * inverse_screen)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
gfx_parse_mode_str(char * str,int * x,int * y,int * depth)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
gfx_fb_color_map(uint8_t index)207 gfx_fb_color_map(uint8_t index)
208 {
209 	return (rgb_color_map(&rgb_info, index, 0xff));
210 }
211 
212 static bool
color_name_to_ansi(const char * name,int * val)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
gfx_set_colors(struct env_var * ev,int flags,const void * value)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
gfx_set_inverses(struct env_var * ev,int flags,const void * value)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
gfx_framework_init(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
rgb_to_color_index(uint8_t r,uint8_t g,uint8_t b)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
gfx_mem_wr1(uint8_t * base,size_t size,uint32_t o,uint8_t v)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
gfx_mem_wr2(uint8_t * base,size_t size,uint32_t o,uint16_t v)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
gfx_mem_wr4(uint8_t * base,size_t size,uint32_t o,uint32_t v)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
gfxfb_blt_fill(void * BltBuffer,uint32_t DestinationX,uint32_t DestinationY,uint32_t Width,uint32_t Height)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
gfxfb_blt_video_to_buffer(void * BltBuffer,uint32_t SourceX,uint32_t SourceY,uint32_t DestinationX,uint32_t DestinationY,uint32_t Width,uint32_t Height,uint32_t Delta)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
gfxfb_blt_buffer_to_video(void * BltBuffer,uint32_t SourceX,uint32_t SourceY,uint32_t DestinationX,uint32_t DestinationY,uint32_t Width,uint32_t Height,uint32_t Delta)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
gfxfb_blt_video_to_video(uint32_t SourceX,uint32_t SourceY,uint32_t DestinationX,uint32_t DestinationY,uint32_t Width,uint32_t Height)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
gfxfb_blt(void * BltBuffer,GFXFB_BLT_OPERATION BltOperation,uint32_t SourceX,uint32_t SourceY,uint32_t DestinationX,uint32_t DestinationY,uint32_t Width,uint32_t Height,uint32_t Delta)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 	EFI_TPL tpl;
840 	extern EFI_GRAPHICS_OUTPUT *gop;
841 
842 	/*
843 	 * We assume Blt() does work, if not, we will need to build
844 	 * exception list case by case.
845 	 * Once boot services are off, we can not use GOP Blt().
846 	 */
847 	if (gop != NULL && has_boot_services) {
848 		tpl = BS->RaiseTPL(TPL_NOTIFY);
849 		switch (BltOperation) {
850 		case GfxFbBltVideoFill:
851 			status = gop->Blt(gop, BltBuffer, EfiBltVideoFill,
852 			    SourceX, SourceY, DestinationX, DestinationY,
853 			    Width, Height, Delta);
854 			break;
855 
856 		case GfxFbBltVideoToBltBuffer:
857 			status = gop->Blt(gop, BltBuffer,
858 			    EfiBltVideoToBltBuffer,
859 			    SourceX, SourceY, DestinationX, DestinationY,
860 			    Width, Height, Delta);
861 			break;
862 
863 		case GfxFbBltBufferToVideo:
864 			status = gop->Blt(gop, BltBuffer, EfiBltBufferToVideo,
865 			    SourceX, SourceY, DestinationX, DestinationY,
866 			    Width, Height, Delta);
867 			break;
868 
869 		case GfxFbBltVideoToVideo:
870 			status = gop->Blt(gop, BltBuffer, EfiBltVideoToVideo,
871 			    SourceX, SourceY, DestinationX, DestinationY,
872 			    Width, Height, Delta);
873 			break;
874 
875 		default:
876 			status = EFI_INVALID_PARAMETER;
877 			break;
878 		}
879 
880 		switch (status) {
881 		case EFI_SUCCESS:
882 			rv = 0;
883 			break;
884 
885 		case EFI_INVALID_PARAMETER:
886 			rv = EINVAL;
887 			break;
888 
889 		case EFI_DEVICE_ERROR:
890 		default:
891 			rv = EIO;
892 			break;
893 		}
894 
895 		BS->RestoreTPL(tpl);
896 		return (rv);
897 	}
898 #endif
899 
900 	switch (BltOperation) {
901 	case GfxFbBltVideoFill:
902 		rv = gfxfb_blt_fill(BltBuffer, DestinationX, DestinationY,
903 		    Width, Height);
904 		break;
905 
906 	case GfxFbBltVideoToBltBuffer:
907 		rv = gfxfb_blt_video_to_buffer(BltBuffer, SourceX, SourceY,
908 		    DestinationX, DestinationY, Width, Height, Delta);
909 		break;
910 
911 	case GfxFbBltBufferToVideo:
912 		rv = gfxfb_blt_buffer_to_video(BltBuffer, SourceX, SourceY,
913 		    DestinationX, DestinationY, Width, Height, Delta);
914 		break;
915 
916 	case GfxFbBltVideoToVideo:
917 		rv = gfxfb_blt_video_to_video(SourceX, SourceY,
918 		    DestinationX, DestinationY, Width, Height);
919 		break;
920 
921 	default:
922 		rv = EINVAL;
923 		break;
924 	}
925 	return (rv);
926 }
927 
928 /*
929  * visual io callbacks.
930  */
931 int
gfx_fb_cons_clear(struct vis_consclear * ca)932 gfx_fb_cons_clear(struct vis_consclear *ca)
933 {
934 	int rv;
935 	uint32_t width, height;
936 
937 	width = gfx_fb.framebuffer_common.framebuffer_width;
938 	height = gfx_fb.framebuffer_common.framebuffer_height;
939 
940 	rv = gfxfb_blt(&ca->bg_color, GfxFbBltVideoFill, 0, 0,
941 	    0, 0, width, height, 0);
942 
943 	return (rv);
944 }
945 
946 void
gfx_fb_cons_copy(struct vis_conscopy * ma)947 gfx_fb_cons_copy(struct vis_conscopy *ma)
948 {
949 	uint32_t width, height;
950 
951 	width = ma->e_col - ma->s_col + 1;
952 	height = ma->e_row - ma->s_row + 1;
953 
954 	(void) gfxfb_blt(NULL, GfxFbBltVideoToVideo, ma->s_col, ma->s_row,
955 	    ma->t_col, ma->t_row, width, height, 0);
956 }
957 
958 /*
959  * Implements alpha blending for RGBA data, could use pixels for arguments,
960  * but byte stream seems more generic.
961  * The generic alpha blending is:
962  * blend = alpha * fg + (1.0 - alpha) * bg.
963  * Since our alpha is not from range [0..1], we scale appropriately.
964  */
965 static uint8_t
alpha_blend(uint8_t fg,uint8_t bg,uint8_t alpha)966 alpha_blend(uint8_t fg, uint8_t bg, uint8_t alpha)
967 {
968 	uint16_t blend, h, l;
969 	uint8_t max_alpha;
970 
971 	/* 15/16 bit depths have alpha channel size less than 8 */
972 	max_alpha = (1 << (rgb_info.red.size + rgb_info.green.size +
973 	    rgb_info.blue.size) / 3) - 1;
974 
975 	/* trivial corner cases */
976 	if (alpha == 0)
977 		return (bg);
978 	if (alpha >= max_alpha)
979 		return (fg);
980 	blend = (alpha * fg + (max_alpha - alpha) * bg);
981 	/* Division by max_alpha */
982 	h = blend >> 8;
983 	l = blend & max_alpha;
984 	if (h + l >= max_alpha)
985 		h++;
986 	return (h);
987 }
988 
989 /* Copy memory to framebuffer or to memory. */
990 static void
bitmap_cpy(void * dst,void * src,size_t size)991 bitmap_cpy(void *dst, void *src, size_t size)
992 {
993 #if defined(EFI)
994 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ps, *pd;
995 #else
996 	struct paletteentry *ps, *pd;
997 #endif
998 	uint32_t i;
999 	uint8_t a;
1000 
1001 	ps = src;
1002 	pd = dst;
1003 
1004 	for (i = 0; i < size; i++) {
1005 		a = ps[i].Reserved;
1006 		pd[i].Red = alpha_blend(ps[i].Red, pd[i].Red, a);
1007 		pd[i].Green = alpha_blend(ps[i].Green, pd[i].Green, a);
1008 		pd[i].Blue = alpha_blend(ps[i].Blue, pd[i].Blue, a);
1009 		pd[i].Reserved = a;
1010 	}
1011 }
1012 
1013 static void *
allocate_glyphbuffer(uint32_t width,uint32_t height)1014 allocate_glyphbuffer(uint32_t width, uint32_t height)
1015 {
1016 	size_t size;
1017 
1018 	size = sizeof (*GlyphBuffer) * width * height;
1019 	if (size != GlyphBufferSize) {
1020 		free(GlyphBuffer);
1021 		GlyphBuffer = malloc(size);
1022 		if (GlyphBuffer == NULL)
1023 			return (NULL);
1024 		GlyphBufferSize = size;
1025 	}
1026 	return (GlyphBuffer);
1027 }
1028 
1029 void
gfx_fb_cons_display(struct vis_consdisplay * da)1030 gfx_fb_cons_display(struct vis_consdisplay *da)
1031 {
1032 #if defined(EFI)
1033 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer;
1034 #else
1035 	struct paletteentry *BltBuffer;
1036 #endif
1037 	uint32_t size;
1038 
1039 	/* make sure we will not write past FB */
1040 	if ((uint32_t)da->col >= gfx_fb.framebuffer_common.framebuffer_width ||
1041 	    (uint32_t)da->row >= gfx_fb.framebuffer_common.framebuffer_height ||
1042 	    (uint32_t)da->col + da->width >
1043 	    gfx_fb.framebuffer_common.framebuffer_width ||
1044 	    (uint32_t)da->row + da->height >
1045 	    gfx_fb.framebuffer_common.framebuffer_height)
1046 		return;
1047 
1048 	size = sizeof (*BltBuffer) * da->width * da->height;
1049 
1050 	/*
1051 	 * Common data to display is glyph, use preallocated
1052 	 * glyph buffer.
1053 	 */
1054 	if (tems.ts_pix_data_size != GlyphBufferSize)
1055 		(void) allocate_glyphbuffer(da->width, da->height);
1056 
1057 	if (size == GlyphBufferSize) {
1058 		BltBuffer = GlyphBuffer;
1059 	} else {
1060 		BltBuffer = malloc(size);
1061 	}
1062 	if (BltBuffer == NULL)
1063 		return;
1064 
1065 	if (gfxfb_blt(BltBuffer, GfxFbBltVideoToBltBuffer,
1066 	    da->col, da->row, 0, 0, da->width, da->height, 0) == 0) {
1067 		bitmap_cpy(BltBuffer, da->data, da->width * da->height);
1068 		(void) gfxfb_blt(BltBuffer, GfxFbBltBufferToVideo,
1069 		    0, 0, da->col, da->row, da->width, da->height, 0);
1070 	}
1071 
1072 	if (BltBuffer != GlyphBuffer)
1073 		free(BltBuffer);
1074 }
1075 
1076 static void
gfx_fb_cursor_impl(uint32_t fg,uint32_t bg,struct vis_conscursor * ca)1077 gfx_fb_cursor_impl(uint32_t fg, uint32_t bg, struct vis_conscursor *ca)
1078 {
1079 	union pixel {
1080 #if defined(EFI)
1081 		EFI_GRAPHICS_OUTPUT_BLT_PIXEL p;
1082 #else
1083 		struct paletteentry p;
1084 #endif
1085 		uint32_t p32;
1086 	} *row;
1087 
1088 	/*
1089 	 * Build inverse image of the glyph.
1090 	 * Since xor has self-inverse property, drawing cursor
1091 	 * second time on the same spot, will restore the original content.
1092 	 */
1093 	for (screen_size_t i = 0; i < ca->height; i++) {
1094 		row = (union pixel *)(GlyphBuffer + i * ca->width);
1095 		for (screen_size_t j = 0; j < ca->width; j++) {
1096 			row[j].p32 = (row[j].p32 ^ fg) ^ bg;
1097 		}
1098 	}
1099 }
1100 
1101 void
gfx_fb_display_cursor(struct vis_conscursor * ca)1102 gfx_fb_display_cursor(struct vis_conscursor *ca)
1103 {
1104 	union pixel {
1105 #if defined(EFI)
1106 		EFI_GRAPHICS_OUTPUT_BLT_PIXEL p;
1107 #else
1108 		struct paletteentry p;
1109 #endif
1110 		uint32_t p32;
1111 	} fg, bg;
1112 
1113 	bcopy(&ca->fg_color, &fg.p32, sizeof (fg.p32));
1114 	bcopy(&ca->bg_color, &bg.p32, sizeof (bg.p32));
1115 
1116 	if (allocate_glyphbuffer(ca->width, ca->height) != NULL) {
1117 		if (gfxfb_blt(GlyphBuffer, GfxFbBltVideoToBltBuffer,
1118 		    ca->col, ca->row, 0, 0, ca->width, ca->height, 0) == 0)
1119 			gfx_fb_cursor_impl(fg.p32, bg.p32, ca);
1120 
1121 		(void) gfxfb_blt(GlyphBuffer, GfxFbBltBufferToVideo, 0, 0,
1122 		    ca->col, ca->row, ca->width, ca->height, 0);
1123 	}
1124 }
1125 
1126 /*
1127  * Public graphics primitives.
1128  */
1129 
1130 static int
isqrt(int num)1131 isqrt(int num)
1132 {
1133 	int res = 0;
1134 	int bit = 1 << 30;
1135 
1136 	/* "bit" starts at the highest power of four <= the argument. */
1137 	while (bit > num)
1138 		bit >>= 2;
1139 
1140 	while (bit != 0) {
1141 		if (num >= res + bit) {
1142 			num -= res + bit;
1143 			res = (res >> 1) + bit;
1144 		} else
1145 			res >>= 1;
1146 		bit >>= 2;
1147 	}
1148 	return (res);
1149 }
1150 
1151 /* set pixel in framebuffer using gfx coordinates */
1152 void
gfx_fb_setpixel(uint32_t x,uint32_t y)1153 gfx_fb_setpixel(uint32_t x, uint32_t y)
1154 {
1155 	text_color_t fg, bg;
1156 
1157 	if (plat_stdout_is_framebuffer() == 0)
1158 		return;
1159 
1160 	tem_get_colors((tem_vt_state_t)tems.ts_active, &fg, &bg);
1161 
1162 	if (x >= gfx_fb.framebuffer_common.framebuffer_width ||
1163 	    y >= gfx_fb.framebuffer_common.framebuffer_height)
1164 		return;
1165 
1166 	gfxfb_blt(&fg.n, GfxFbBltVideoFill, 0, 0, x, y, 1, 1, 0);
1167 }
1168 
1169 /*
1170  * draw rectangle in framebuffer using gfx coordinates.
1171  */
1172 void
gfx_fb_drawrect(uint32_t x1,uint32_t y1,uint32_t x2,uint32_t y2,uint32_t fill)1173 gfx_fb_drawrect(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2,
1174     uint32_t fill)
1175 {
1176 	text_color_t fg, bg;
1177 
1178 	if (plat_stdout_is_framebuffer() == 0)
1179 		return;
1180 
1181 	tem_get_colors((tem_vt_state_t)tems.ts_active, &fg, &bg);
1182 
1183 	if (fill != 0) {
1184 		gfxfb_blt(&fg.n, GfxFbBltVideoFill,
1185 		    0, 0, x1, y1, x2 - x1, y2 - y1, 0);
1186 	} else {
1187 		gfxfb_blt(&fg.n, GfxFbBltVideoFill,
1188 		    0, 0, x1, y1, x2 - x1, 1, 0);
1189 		gfxfb_blt(&fg.n, GfxFbBltVideoFill,
1190 		    0, 0, x1, y2, x2 - x1, 1, 0);
1191 		gfxfb_blt(&fg.n, GfxFbBltVideoFill,
1192 		    0, 0, x1, y1, 1, y2 - y1, 0);
1193 		gfxfb_blt(&fg.n, GfxFbBltVideoFill,
1194 		    0, 0, x2, y1, 1, y2 - y1, 0);
1195 	}
1196 }
1197 
1198 void
gfx_fb_line(uint32_t x0,uint32_t y0,uint32_t x1,uint32_t y1,uint32_t wd)1199 gfx_fb_line(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t wd)
1200 {
1201 	int dx, sx, dy, sy;
1202 	int err, e2, x2, y2, ed, width;
1203 
1204 	if (plat_stdout_is_framebuffer() == 0)
1205 		return;
1206 
1207 	width = wd;
1208 	sx = x0 < x1? 1 : -1;
1209 	sy = y0 < y1? 1 : -1;
1210 	dx = x1 > x0? x1 - x0 : x0 - x1;
1211 	dy = y1 > y0? y1 - y0 : y0 - y1;
1212 	err = dx + dy;
1213 	ed = dx + dy == 0 ? 1: isqrt(dx * dx + dy * dy);
1214 
1215 	for (;;) {
1216 		gfx_fb_setpixel(x0, y0);
1217 		e2 = err;
1218 		x2 = x0;
1219 		if ((e2 << 1) >= -dx) {		/* x step */
1220 			e2 += dy;
1221 			y2 = y0;
1222 			while (e2 < ed * width &&
1223 			    (y1 != (uint32_t)y2 || dx > dy)) {
1224 				y2 += sy;
1225 				gfx_fb_setpixel(x0, y2);
1226 				e2 += dx;
1227 			}
1228 			if (x0 == x1)
1229 				break;
1230 			e2 = err;
1231 			err -= dy;
1232 			x0 += sx;
1233 		}
1234 		if ((e2 << 1) <= dy) {		/* y step */
1235 			e2 = dx-e2;
1236 			while (e2 < ed * width &&
1237 			    (x1 != (uint32_t)x2 || dx < dy)) {
1238 				x2 += sx;
1239 				gfx_fb_setpixel(x2, y0);
1240 				e2 += dy;
1241 			}
1242 			if (y0 == y1)
1243 				break;
1244 			err += dx;
1245 			y0 += sy;
1246 		}
1247 	}
1248 }
1249 
1250 /*
1251  * quadratic Bézier curve limited to gradients without sign change.
1252  */
1253 void
gfx_fb_bezier(uint32_t x0,uint32_t y0,uint32_t x1,uint32_t y1,uint32_t x2,uint32_t y2,uint32_t wd)1254 gfx_fb_bezier(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t x2,
1255     uint32_t y2, uint32_t wd)
1256 {
1257 	int sx, sy, xx, yy, xy, width;
1258 	int dx, dy, err, curvature;
1259 	int i;
1260 
1261 	if (plat_stdout_is_framebuffer() == 0)
1262 		return;
1263 
1264 	width = wd;
1265 	sx = x2 - x1;
1266 	sy = y2 - y1;
1267 	xx = x0 - x1;
1268 	yy = y0 - y1;
1269 	curvature = xx*sy - yy*sx;
1270 
1271 	if (sx*sx + sy*sy > xx*xx+yy*yy) {
1272 		x2 = x0;
1273 		x0 = sx + x1;
1274 		y2 = y0;
1275 		y0 = sy + y1;
1276 		curvature = -curvature;
1277 	}
1278 	if (curvature != 0) {
1279 		xx += sx;
1280 		sx = x0 < x2? 1 : -1;
1281 		xx *= sx;
1282 		yy += sy;
1283 		sy = y0 < y2? 1 : -1;
1284 		yy *= sy;
1285 		xy = (xx*yy) << 1;
1286 		xx *= xx;
1287 		yy *= yy;
1288 		if (curvature * sx * sy < 0) {
1289 			xx = -xx;
1290 			yy = -yy;
1291 			xy = -xy;
1292 			curvature = -curvature;
1293 		}
1294 		dx = 4 * sy * curvature * (x1 - x0) + xx - xy;
1295 		dy = 4 * sx * curvature * (y0 - y1) + yy - xy;
1296 		xx += xx;
1297 		yy += yy;
1298 		err = dx + dy + xy;
1299 		do {
1300 			for (i = 0; i <= width; i++)
1301 				gfx_fb_setpixel(x0 + i, y0);
1302 			if (x0 == x2 && y0 == y2)
1303 				return;  /* last pixel -> curve finished */
1304 			y1 = 2 * err < dx;
1305 			if (2 * err > dy) {
1306 				x0 += sx;
1307 				dx -= xy;
1308 				dy += yy;
1309 				err += dy;
1310 			}
1311 			if (y1 != 0) {
1312 				y0 += sy;
1313 				dy -= xy;
1314 				dx += xx;
1315 				err += dx;
1316 			}
1317 		} while (dy < dx); /* gradient negates -> algorithm fails */
1318 	}
1319 	gfx_fb_line(x0, y0, x2, y2, width);
1320 }
1321 
1322 /*
1323  * draw rectangle using terminal coordinates and current foreground color.
1324  */
1325 void
gfx_term_drawrect(uint32_t ux1,uint32_t uy1,uint32_t ux2,uint32_t uy2)1326 gfx_term_drawrect(uint32_t ux1, uint32_t uy1, uint32_t ux2, uint32_t uy2)
1327 {
1328 	int x1, y1, x2, y2;
1329 	int xshift, yshift;
1330 	int width, i;
1331 	uint32_t vf_width, vf_height;
1332 
1333 	if (plat_stdout_is_framebuffer() == 0)
1334 		return;
1335 
1336 	vf_width = tems.ts_font.vf_width;
1337 	vf_height = tems.ts_font.vf_height;
1338 	width = vf_width / 4;			/* line width */
1339 	xshift = (vf_width - width) / 2;
1340 	yshift = (vf_height - width) / 2;
1341 
1342 	/* Shift coordinates */
1343 	if (ux1 != 0)
1344 		ux1--;
1345 	if (uy1 != 0)
1346 		uy1--;
1347 	ux2--;
1348 	uy2--;
1349 
1350 	/* mark area used in tem */
1351 	tem_image_display(tems.ts_active, uy1, ux1, uy2 + 1, ux2 + 1);
1352 
1353 	/*
1354 	 * Draw horizontal lines width points thick, shifted from outer edge.
1355 	 */
1356 	x1 = (ux1 + 1) * vf_width + tems.ts_p_offset.x;
1357 	y1 = uy1 * vf_height + tems.ts_p_offset.y + yshift;
1358 	x2 = ux2 * vf_width + tems.ts_p_offset.x;
1359 	gfx_fb_drawrect(x1, y1, x2, y1 + width, 1);
1360 	y2 = uy2 * vf_height + tems.ts_p_offset.y;
1361 	y2 += vf_height - yshift - width;
1362 	gfx_fb_drawrect(x1, y2, x2, y2 + width, 1);
1363 
1364 	/*
1365 	 * Draw vertical lines width points thick, shifted from outer edge.
1366 	 */
1367 	x1 = ux1 * vf_width + tems.ts_p_offset.x + xshift;
1368 	y1 = uy1 * vf_height + tems.ts_p_offset.y;
1369 	y1 += vf_height;
1370 	y2 = uy2 * vf_height + tems.ts_p_offset.y;
1371 	gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
1372 	x1 = ux2 * vf_width + tems.ts_p_offset.x;
1373 	x1 += vf_width - xshift - width;
1374 	gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
1375 
1376 	/* Draw upper left corner. */
1377 	x1 = ux1 * vf_width + tems.ts_p_offset.x + xshift;
1378 	y1 = uy1 * vf_height + tems.ts_p_offset.y;
1379 	y1 += vf_height;
1380 
1381 	x2 = ux1 * vf_width + tems.ts_p_offset.x;
1382 	x2 += vf_width;
1383 	y2 = uy1 * vf_height + tems.ts_p_offset.y + yshift;
1384 	for (i = 0; i <= width; i++)
1385 		gfx_fb_bezier(x1 + i, y1, x1 + i, y2 + i, x2, y2 + i, width-i);
1386 
1387 	/* Draw lower left corner. */
1388 	x1 = ux1 * vf_width + tems.ts_p_offset.x;
1389 	x1 += vf_width;
1390 	y1 = uy2 * vf_height + tems.ts_p_offset.y;
1391 	y1 += vf_height - yshift;
1392 	x2 = ux1 * vf_width + tems.ts_p_offset.x + xshift;
1393 	y2 = uy2 * vf_height + tems.ts_p_offset.y;
1394 	for (i = 0; i <= width; i++)
1395 		gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
1396 
1397 	/* Draw upper right corner. */
1398 	x1 = ux2 * vf_width + tems.ts_p_offset.x;
1399 	y1 = uy1 * vf_height + tems.ts_p_offset.y + yshift;
1400 	x2 = ux2 * vf_width + tems.ts_p_offset.x;
1401 	x2 += vf_width - xshift - width;
1402 	y2 = uy1 * vf_height + tems.ts_p_offset.y;
1403 	y2 += vf_height;
1404 	for (i = 0; i <= width; i++)
1405 		gfx_fb_bezier(x1, y1 + i, x2 + i, y1 + i, x2 + i, y2, width-i);
1406 
1407 	/* Draw lower right corner. */
1408 	x1 = ux2 * vf_width + tems.ts_p_offset.x;
1409 	y1 = uy2 * vf_height + tems.ts_p_offset.y;
1410 	y1 += vf_height - yshift;
1411 	x2 = ux2 * vf_width + tems.ts_p_offset.x;
1412 	x2 += vf_width - xshift - width;
1413 	y2 = uy2 * vf_height + tems.ts_p_offset.y;
1414 	for (i = 0; i <= width; i++)
1415 		gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
1416 }
1417 
1418 int
gfx_fb_putimage(png_t * png,uint32_t ux1,uint32_t uy1,uint32_t ux2,uint32_t uy2,uint32_t flags)1419 gfx_fb_putimage(png_t *png, uint32_t ux1, uint32_t uy1, uint32_t ux2,
1420     uint32_t uy2, uint32_t flags)
1421 {
1422 #if defined(EFI)
1423 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
1424 #else
1425 	struct paletteentry *p;
1426 #endif
1427 	struct vis_consdisplay da;
1428 	uint32_t i, j, x, y, fheight, fwidth;
1429 	uint8_t r, g, b, a;
1430 	bool scale = false;
1431 	bool trace = false;
1432 
1433 	trace = (flags & FL_PUTIMAGE_DEBUG) != 0;
1434 
1435 	if (plat_stdout_is_framebuffer() == 0) {
1436 		if (trace)
1437 			printf("Framebuffer not active.\n");
1438 		return (1);
1439 	}
1440 
1441 	if (png->color_type != PNG_TRUECOLOR_ALPHA) {
1442 		if (trace)
1443 			printf("Not truecolor image.\n");
1444 		return (1);
1445 	}
1446 
1447 	if (ux1 > gfx_fb.framebuffer_common.framebuffer_width ||
1448 	    uy1 > gfx_fb.framebuffer_common.framebuffer_height) {
1449 		if (trace)
1450 			printf("Top left coordinate off screen.\n");
1451 		return (1);
1452 	}
1453 
1454 	if (png->width > UINT16_MAX || png->height > UINT16_MAX) {
1455 		if (trace)
1456 			printf("Image too large.\n");
1457 		return (1);
1458 	}
1459 
1460 	if (png->width < 1 || png->height < 1) {
1461 		if (trace)
1462 			printf("Image too small.\n");
1463 		return (1);
1464 	}
1465 
1466 	/*
1467 	 * If 0 was passed for either ux2 or uy2, then calculate the missing
1468 	 * part of the bottom right coordinate.
1469 	 */
1470 	scale = true;
1471 	if (ux2 == 0 && uy2 == 0) {
1472 		/* Both 0, use the native resolution of the image */
1473 		ux2 = ux1 + png->width;
1474 		uy2 = uy1 + png->height;
1475 		scale = false;
1476 	} else if (ux2 == 0) {
1477 		/* Set ux2 from uy2/uy1 to maintain aspect ratio */
1478 		ux2 = ux1 + (png->width * (uy2 - uy1)) / png->height;
1479 	} else if (uy2 == 0) {
1480 		/* Set uy2 from ux2/ux1 to maintain aspect ratio */
1481 		uy2 = uy1 + (png->height * (ux2 - ux1)) / png->width;
1482 	}
1483 
1484 	if (ux2 > gfx_fb.framebuffer_common.framebuffer_width ||
1485 	    uy2 > gfx_fb.framebuffer_common.framebuffer_height) {
1486 		if (trace)
1487 			printf("Bottom right coordinate off screen.\n");
1488 		return (1);
1489 	}
1490 
1491 	fwidth = ux2 - ux1;
1492 	fheight = uy2 - uy1;
1493 
1494 	/*
1495 	 * If the original image dimensions have been passed explicitly,
1496 	 * disable scaling.
1497 	 */
1498 	if (fwidth == png->width && fheight == png->height)
1499 		scale = false;
1500 
1501 	if (ux1 == 0) {
1502 		/*
1503 		 * No top left X co-ordinate (real coordinates start at 1),
1504 		 * place as far right as it will fit.
1505 		 */
1506 		ux2 = gfx_fb.framebuffer_common.framebuffer_width -
1507 		    tems.ts_p_offset.x;
1508 		ux1 = ux2 - fwidth;
1509 	}
1510 
1511 	if (uy1 == 0) {
1512 		/*
1513 		 * No top left Y co-ordinate (real coordinates start at 1),
1514 		 * place as far down as it will fit.
1515 		 */
1516 		uy2 = gfx_fb.framebuffer_common.framebuffer_height -
1517 		    tems.ts_p_offset.y;
1518 		uy1 = uy2 - fheight;
1519 	}
1520 
1521 	if (ux1 >= ux2 || uy1 >= uy2) {
1522 		if (trace)
1523 			printf("Image dimensions reversed.\n");
1524 		return (1);
1525 	}
1526 
1527 	if (fwidth < 2 || fheight < 2) {
1528 		if (trace)
1529 			printf("Target area too small\n");
1530 		return (1);
1531 	}
1532 
1533 	if (trace)
1534 		printf("Image %ux%u -> %ux%u @%ux%u\n",
1535 		    png->width, png->height, fwidth, fheight, ux1, uy1);
1536 
1537 	da.col = ux1;
1538 	da.row = uy1;
1539 	da.width = fwidth;
1540 	da.height = fheight;
1541 
1542 	/*
1543 	 * mark area used in tem
1544 	 */
1545 	if (!(flags & FL_PUTIMAGE_NOSCROLL)) {
1546 		tem_image_display(tems.ts_active,
1547 		    da.row / tems.ts_font.vf_height,
1548 		    da.col / tems.ts_font.vf_width,
1549 		    (da.row + da.height) / tems.ts_font.vf_height,
1550 		    (da.col + da.width) / tems.ts_font.vf_width);
1551 	}
1552 
1553 	if ((flags & FL_PUTIMAGE_BORDER))
1554 		gfx_fb_drawrect(ux1, uy1, ux2, uy2, 0);
1555 
1556 	da.data = malloc(fwidth * fheight * sizeof (*p));
1557 	p = (void *)da.data;
1558 	if (da.data == NULL) {
1559 		if (trace)
1560 			printf("Out of memory.\n");
1561 		return (1);
1562 	}
1563 
1564 	/*
1565 	 * Build image for our framebuffer.
1566 	 */
1567 
1568 	/* Helper to calculate the pixel index from the source png */
1569 #define	GETPIXEL(xx, yy) (((yy) * png->width + (xx)) * png->bpp)
1570 
1571 	/*
1572 	 * For each of the x and y directions, calculate the number of pixels
1573 	 * in the source image that correspond to a single pixel in the target.
1574 	 * Use fixed-point arithmetic with 16-bits for each of the integer and
1575 	 * fractional parts.
1576 	 */
1577 	const uint32_t wcstep = ((png->width - 1) << 16) / (fwidth - 1);
1578 	const uint32_t hcstep = ((png->height - 1) << 16) / (fheight - 1);
1579 
1580 	uint32_t hc = 0;
1581 	for (y = 0; y < fheight; y++) {
1582 		uint32_t hc2 = (hc >> 9) & 0x7f;
1583 		uint32_t hc1 = 0x80 - hc2;
1584 
1585 		uint32_t offset_y = hc >> 16;
1586 		uint32_t offset_y1 = offset_y + 1;
1587 
1588 		uint32_t wc = 0;
1589 		for (x = 0; x < fwidth; x++) {
1590 			uint32_t wc2 = (wc >> 9) & 0x7f;
1591 			uint32_t wc1 = 0x80 - wc2;
1592 
1593 			uint32_t offset_x = wc >> 16;
1594 			uint32_t offset_x1 = offset_x + 1;
1595 
1596 			/* Target pixel index */
1597 			j = y * fwidth + x;
1598 
1599 			if (!scale) {
1600 				i = GETPIXEL(x, y);
1601 				r = png->image[i];
1602 				g = png->image[i + 1];
1603 				b = png->image[i + 2];
1604 				a = png->image[i + 3];
1605 			} else {
1606 				uint8_t pixel[4];
1607 
1608 				uint32_t p00 = GETPIXEL(offset_x, offset_y);
1609 				uint32_t p01 = GETPIXEL(offset_x, offset_y1);
1610 				uint32_t p10 = GETPIXEL(offset_x1, offset_y);
1611 				uint32_t p11 = GETPIXEL(offset_x1, offset_y1);
1612 
1613 				/*
1614 				 * Given a 2x2 array of pixels in the source
1615 				 * image, combine them to produce a single
1616 				 * value for the pixel in the target image.
1617 				 * Each column of pixels is combined using
1618 				 * a weighted average where the top and bottom
1619 				 * pixels contribute hc1 and hc2 respectively.
1620 				 * The calculation for bottom pixel pB and
1621 				 * top pixel pT is:
1622 				 *   (pT * hc1 + pB * hc2) / (hc1 + hc2)
1623 				 * Once the values are determined for the two
1624 				 * columns of pixels, then the columns are
1625 				 * averaged together in the same way but using
1626 				 * wc1 and wc2 for the weightings.
1627 				 *
1628 				 * Since hc1 and hc2 are chosen so that
1629 				 * hc1 + hc2 == 128 (and same for wc1 + wc2),
1630 				 * the >> 14 below is a quick way to divide by
1631 				 * (hc1 + hc2) * (wc1 + wc2)
1632 				 */
1633 				for (i = 0; i < 4; i++)
1634 					pixel[i] = (
1635 					    (png->image[p00 + i] * hc1 +
1636 					    png->image[p01 + i] * hc2) * wc1 +
1637 					    (png->image[p10 + i] * hc1 +
1638 					    png->image[p11 + i] * hc2) * wc2)
1639 					    >> 14;
1640 
1641 				r = pixel[0];
1642 				g = pixel[1];
1643 				b = pixel[2];
1644 				a = pixel[3];
1645 			}
1646 
1647 			if (trace)
1648 				printf("r/g/b: %x/%x/%x\n", r, g, b);
1649 			/*
1650 			 * Rough colorspace reduction for 15/16 bit colors.
1651 			 */
1652 			p[j].Red = r >>
1653 			    (8 - gfx_fb.u.fb2.framebuffer_red_mask_size);
1654 			p[j].Green = g >>
1655 			    (8 - gfx_fb.u.fb2.framebuffer_green_mask_size);
1656 			p[j].Blue = b >>
1657 			    (8 - gfx_fb.u.fb2.framebuffer_blue_mask_size);
1658 			p[j].Reserved = a;
1659 
1660 			wc += wcstep;
1661 		}
1662 		hc += hcstep;
1663 	}
1664 
1665 	gfx_fb_cons_display(&da);
1666 	free(da.data);
1667 	return (0);
1668 }
1669 
1670 /* Return  w^2 + h^2 or 0, if the dimensions are unknown */
1671 static unsigned
edid_diagonal_squared(void)1672 edid_diagonal_squared(void)
1673 {
1674 	unsigned w, h;
1675 
1676 	if (edid_info == NULL)
1677 		return (0);
1678 
1679 	w = edid_info->display.max_horizontal_image_size;
1680 	h = edid_info->display.max_vertical_image_size;
1681 
1682 	/* If either one is 0, we have aspect ratio, not size */
1683 	if (w == 0 || h == 0)
1684 		return (0);
1685 
1686 	/*
1687 	 * some monitors encode the aspect ratio instead of the physical size.
1688 	 */
1689 	if ((w == 16 && h == 9) || (w == 16 && h == 10) ||
1690 	    (w == 4 && h == 3) || (w == 5 && h == 4))
1691 		return (0);
1692 
1693 	/*
1694 	 * translate cm to inch, note we scale by 100 here.
1695 	 */
1696 	w = w * 100 / 254;
1697 	h = h * 100 / 254;
1698 
1699 	/* Return w^2 + h^2 */
1700 	return (w * w + h * h);
1701 }
1702 
1703 /*
1704  * calculate pixels per inch.
1705  */
1706 static unsigned
gfx_get_ppi(void)1707 gfx_get_ppi(void)
1708 {
1709 	unsigned dp, di;
1710 
1711 	di = edid_diagonal_squared();
1712 	if (di == 0)
1713 		return (0);
1714 
1715 	dp = gfx_fb.framebuffer_common.framebuffer_width *
1716 	    gfx_fb.framebuffer_common.framebuffer_width +
1717 	    gfx_fb.framebuffer_common.framebuffer_height *
1718 	    gfx_fb.framebuffer_common.framebuffer_height;
1719 
1720 	return (isqrt(dp / di));
1721 }
1722 
1723 /*
1724  * Calculate font size from density independent pixels (dp):
1725  * ((16dp * ppi) / 160) * display_factor.
1726  * Here we are using fixed constants: 1dp == 160 ppi and
1727  * display_factor 2.
1728  *
1729  * We are rounding font size up and are searching for font which is
1730  * not smaller than calculated size value.
1731  */
1732 bitmap_data_t *
gfx_get_font(void)1733 gfx_get_font(void)
1734 {
1735 	unsigned ppi, size;
1736 	bitmap_data_t *font = NULL;
1737 	struct fontlist *fl, *next;
1738 
1739 	/* Text mode is not supported here. */
1740 	if (gfx_fb.framebuffer_common.framebuffer_type ==
1741 	    MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT)
1742 		return (NULL);
1743 
1744 	ppi = gfx_get_ppi();
1745 	if (ppi == 0)
1746 		return (NULL);
1747 
1748 	/*
1749 	 * We will search for 16dp font.
1750 	 * We are using scale up by 10 for roundup.
1751 	 */
1752 	size = (16 * ppi * 10) / 160;
1753 	/* Apply display factor 2.  */
1754 	size = roundup(size * 2, 10) / 10;
1755 
1756 	STAILQ_FOREACH(fl, &fonts, font_next) {
1757 		next = STAILQ_NEXT(fl, font_next);
1758 		/*
1759 		 * If this is last font or, if next font is smaller,
1760 		 * we have our font. Make sure, it actually is loaded.
1761 		 */
1762 		if (next == NULL || next->font_data->height < size) {
1763 			font = fl->font_data;
1764 			if (font->font == NULL ||
1765 			    fl->font_flags == FONT_RELOAD) {
1766 				if (fl->font_load != NULL &&
1767 				    fl->font_name != NULL)
1768 					font = fl->font_load(fl->font_name);
1769 			}
1770 			break;
1771 		}
1772 	}
1773 
1774 	return (font);
1775 }
1776 
1777 static int
load_mapping(int fd,struct font * fp,int n)1778 load_mapping(int fd, struct font *fp, int n)
1779 {
1780 	size_t i, size;
1781 	ssize_t rv;
1782 	struct font_map *mp;
1783 
1784 	if (fp->vf_map_count[n] == 0)
1785 		return (0);
1786 
1787 	size = fp->vf_map_count[n] * sizeof (*mp);
1788 	mp = malloc(size);
1789 	if (mp == NULL)
1790 		return (ENOMEM);
1791 	fp->vf_map[n] = mp;
1792 
1793 	rv = read(fd, mp, size);
1794 	if (rv < 0 || (size_t)rv != size) {
1795 		free(fp->vf_map[n]);
1796 		fp->vf_map[n] = NULL;
1797 		return (EIO);
1798 	}
1799 
1800 	for (i = 0; i < fp->vf_map_count[n]; i++) {
1801 		mp[i].font_src = be32toh(mp[i].font_src);
1802 		mp[i].font_dst = be16toh(mp[i].font_dst);
1803 		mp[i].font_len = be16toh(mp[i].font_len);
1804 	}
1805 	return (0);
1806 }
1807 
1808 static int
builtin_mapping(struct font * fp,int n)1809 builtin_mapping(struct font *fp, int n)
1810 {
1811 	size_t size;
1812 	struct font_map *mp;
1813 
1814 	if (n >= VFNT_MAPS)
1815 		return (EINVAL);
1816 
1817 	if (fp->vf_map_count[n] == 0)
1818 		return (0);
1819 
1820 	size = fp->vf_map_count[n] * sizeof (*mp);
1821 	mp = malloc(size);
1822 	if (mp == NULL)
1823 		return (ENOMEM);
1824 	fp->vf_map[n] = mp;
1825 
1826 	memcpy(mp, DEFAULT_FONT_DATA.font->vf_map[n], size);
1827 	return (0);
1828 }
1829 
1830 /*
1831  * Load font from builtin or from file.
1832  * We do need special case for builtin because the builtin font glyphs
1833  * are compressed and we do need to uncompress them.
1834  * Having single load_font() for both cases will help us to simplify
1835  * font switch handling.
1836  */
1837 static bitmap_data_t *
load_font(char * path)1838 load_font(char *path)
1839 {
1840 	int fd, i;
1841 	uint32_t glyphs;
1842 	struct font_header fh;
1843 	struct fontlist *fl;
1844 	bitmap_data_t *bp;
1845 	struct font *fp;
1846 	size_t size;
1847 	ssize_t rv;
1848 
1849 	/* Get our entry from the font list. */
1850 	STAILQ_FOREACH(fl, &fonts, font_next) {
1851 		if (strcmp(fl->font_name, path) == 0)
1852 			break;
1853 	}
1854 	if (fl == NULL)
1855 		return (NULL);	/* Should not happen. */
1856 
1857 	bp = fl->font_data;
1858 	if (bp->font != NULL && fl->font_flags != FONT_RELOAD)
1859 		return (bp);
1860 
1861 	fd = -1;
1862 	/*
1863 	 * Special case for builtin font.
1864 	 * Builtin font is the very first font we load, we do not have
1865 	 * previous loads to be released.
1866 	 */
1867 	if (fl->font_flags == FONT_BUILTIN) {
1868 		if ((fp = calloc(1, sizeof (struct font))) == NULL)
1869 			return (NULL);
1870 
1871 		fp->vf_width = DEFAULT_FONT_DATA.width;
1872 		fp->vf_height = DEFAULT_FONT_DATA.height;
1873 
1874 		fp->vf_bytes = malloc(DEFAULT_FONT_DATA.uncompressed_size);
1875 		if (fp->vf_bytes == NULL) {
1876 			free(fp);
1877 			return (NULL);
1878 		}
1879 
1880 		bp->uncompressed_size = DEFAULT_FONT_DATA.uncompressed_size;
1881 		bp->compressed_size = DEFAULT_FONT_DATA.compressed_size;
1882 
1883 		if (lz4_decompress(DEFAULT_FONT_DATA.compressed_data,
1884 		    fp->vf_bytes,
1885 		    DEFAULT_FONT_DATA.compressed_size,
1886 		    DEFAULT_FONT_DATA.uncompressed_size, 0) != 0) {
1887 			free(fp->vf_bytes);
1888 			free(fp);
1889 			return (NULL);
1890 		}
1891 
1892 		for (i = 0; i < VFNT_MAPS; i++) {
1893 			fp->vf_map_count[i] =
1894 			    DEFAULT_FONT_DATA.font->vf_map_count[i];
1895 			if (builtin_mapping(fp, i) != 0)
1896 				goto free_done;
1897 		}
1898 
1899 		bp->font = fp;
1900 		return (bp);
1901 	}
1902 
1903 	fd = open(path, O_RDONLY);
1904 	if (fd < 0) {
1905 		return (NULL);
1906 	}
1907 
1908 	size = sizeof (fh);
1909 	rv = read(fd, &fh, size);
1910 	if (rv < 0 || (size_t)rv != size) {
1911 		bp = NULL;
1912 		goto done;
1913 	}
1914 	if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC, sizeof (fh.fh_magic)) != 0) {
1915 		bp = NULL;
1916 		goto done;
1917 	}
1918 	if ((fp = calloc(1, sizeof (struct font))) == NULL) {
1919 		bp = NULL;
1920 		goto done;
1921 	}
1922 	for (i = 0; i < VFNT_MAPS; i++)
1923 		fp->vf_map_count[i] = be32toh(fh.fh_map_count[i]);
1924 
1925 	glyphs = be32toh(fh.fh_glyph_count);
1926 	fp->vf_width = fh.fh_width;
1927 	fp->vf_height = fh.fh_height;
1928 
1929 	size = howmany(fp->vf_width, 8) * fp->vf_height * glyphs;
1930 	bp->uncompressed_size = size;
1931 	if ((fp->vf_bytes = malloc(size)) == NULL)
1932 		goto free_done;
1933 
1934 	rv = read(fd, fp->vf_bytes, size);
1935 	if (rv < 0 || (size_t)rv != size)
1936 		goto free_done;
1937 	for (i = 0; i < VFNT_MAPS; i++) {
1938 		if (load_mapping(fd, fp, i) != 0)
1939 			goto free_done;
1940 	}
1941 
1942 	/*
1943 	 * Reset builtin flag now as we have full font loaded.
1944 	 */
1945 	if (fl->font_flags == FONT_BUILTIN)
1946 		fl->font_flags = FONT_AUTO;
1947 
1948 	/*
1949 	 * Release previously loaded entries. We can do this now, as
1950 	 * the new font is loaded. Note, there can be no console
1951 	 * output till the new font is in place and tem is notified.
1952 	 * We do need to keep fl->font_data for glyph dimensions.
1953 	 */
1954 	STAILQ_FOREACH(fl, &fonts, font_next) {
1955 		if (fl->font_data->font == NULL)
1956 			continue;
1957 
1958 		for (i = 0; i < VFNT_MAPS; i++)
1959 			free(fl->font_data->font->vf_map[i]);
1960 		free(fl->font_data->font->vf_bytes);
1961 		free(fl->font_data->font);
1962 		fl->font_data->font = NULL;
1963 	}
1964 
1965 	bp->font = fp;
1966 	bp->compressed_size = 0;
1967 
1968 done:
1969 	if (fd != -1)
1970 		close(fd);
1971 	return (bp);
1972 
1973 free_done:
1974 	for (i = 0; i < VFNT_MAPS; i++)
1975 		free(fp->vf_map[i]);
1976 	free(fp->vf_bytes);
1977 	free(fp);
1978 	bp = NULL;
1979 	goto done;
1980 }
1981 
1982 
1983 struct name_entry {
1984 	char			*n_name;
1985 	SLIST_ENTRY(name_entry)	n_entry;
1986 };
1987 
1988 SLIST_HEAD(name_list, name_entry);
1989 
1990 /* Read font names from index file. */
1991 static struct name_list *
read_list(char * fonts)1992 read_list(char *fonts)
1993 {
1994 	struct name_list *nl;
1995 	struct name_entry *np;
1996 	char buf[PATH_MAX];
1997 	int fd, len;
1998 
1999 	fd = open(fonts, O_RDONLY);
2000 	if (fd < 0)
2001 		return (NULL);
2002 
2003 	nl = malloc(sizeof (*nl));
2004 	if (nl == NULL) {
2005 		close(fd);
2006 		return (nl);
2007 	}
2008 
2009 	SLIST_INIT(nl);
2010 	while ((len = fgetstr(buf, sizeof (buf), fd)) > 0) {
2011 		np = malloc(sizeof (*np));
2012 		if (np == NULL) {
2013 			close(fd);
2014 			return (nl);    /* return what we have */
2015 		}
2016 		np->n_name = strdup(buf);
2017 		if (np->n_name == NULL) {
2018 			free(np);
2019 			close(fd);
2020 			return (nl);    /* return what we have */
2021 		}
2022 		SLIST_INSERT_HEAD(nl, np, n_entry);
2023 	}
2024 	close(fd);
2025 	return (nl);
2026 }
2027 
2028 /*
2029  * Read the font properties and insert new entry into the list.
2030  * The font list is built in descending order.
2031  */
2032 static bool
insert_font(char * name,FONT_FLAGS flags)2033 insert_font(char *name, FONT_FLAGS flags)
2034 {
2035 	struct font_header fh;
2036 	struct fontlist *fp, *previous, *entry, *next;
2037 	size_t size;
2038 	ssize_t rv;
2039 	int fd;
2040 	char *font_name;
2041 
2042 	font_name = NULL;
2043 	if (flags == FONT_BUILTIN) {
2044 		/*
2045 		 * We only install builtin font once, while setting up
2046 		 * initial console. Since this will happen very early,
2047 		 * we assume asprintf will not fail. Once we have access to
2048 		 * files, the builtin font will be replaced by font loaded
2049 		 * from file.
2050 		 */
2051 		if (!STAILQ_EMPTY(&fonts))
2052 			return (false);
2053 
2054 		fh.fh_width = DEFAULT_FONT_DATA.width;
2055 		fh.fh_height = DEFAULT_FONT_DATA.height;
2056 
2057 		(void) asprintf(&font_name, "%dx%d",
2058 		    DEFAULT_FONT_DATA.width, DEFAULT_FONT_DATA.height);
2059 	} else {
2060 		fd = open(name, O_RDONLY);
2061 		if (fd < 0)
2062 			return (false);
2063 		rv = read(fd, &fh, sizeof (fh));
2064 		close(fd);
2065 		if (rv < 0 || (size_t)rv != sizeof (fh))
2066 			return (false);
2067 
2068 		if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC,
2069 		    sizeof (fh.fh_magic)) != 0)
2070 			return (false);
2071 		font_name = strdup(name);
2072 	}
2073 
2074 	if (font_name == NULL)
2075 		return (false);
2076 
2077 	/*
2078 	 * If we have an entry with the same glyph dimensions, replace
2079 	 * the file name and mark us. We only support unique dimensions.
2080 	 */
2081 	STAILQ_FOREACH(entry, &fonts, font_next) {
2082 		if (fh.fh_width == entry->font_data->width &&
2083 		    fh.fh_height == entry->font_data->height) {
2084 			free(entry->font_name);
2085 			entry->font_name = font_name;
2086 			entry->font_flags = FONT_RELOAD;
2087 			return (true);
2088 		}
2089 	}
2090 
2091 	fp = calloc(sizeof (*fp), 1);
2092 	if (fp == NULL) {
2093 		free(font_name);
2094 		return (false);
2095 	}
2096 	fp->font_data = calloc(sizeof (*fp->font_data), 1);
2097 	if (fp->font_data == NULL) {
2098 		free(font_name);
2099 		free(fp);
2100 		return (false);
2101 	}
2102 	fp->font_name = font_name;
2103 	fp->font_flags = flags;
2104 	fp->font_load = load_font;
2105 	fp->font_data->width = fh.fh_width;
2106 	fp->font_data->height = fh.fh_height;
2107 
2108 	if (STAILQ_EMPTY(&fonts)) {
2109 		STAILQ_INSERT_HEAD(&fonts, fp, font_next);
2110 		return (true);
2111 	}
2112 
2113 	previous = NULL;
2114 	size = fp->font_data->width * fp->font_data->height;
2115 
2116 	STAILQ_FOREACH(entry, &fonts, font_next) {
2117 		/* Should fp be inserted before the entry? */
2118 		if (size > entry->font_data->width * entry->font_data->height) {
2119 			if (previous == NULL) {
2120 				STAILQ_INSERT_HEAD(&fonts, fp, font_next);
2121 			} else {
2122 				STAILQ_INSERT_AFTER(&fonts, previous, fp,
2123 				    font_next);
2124 			}
2125 			return (true);
2126 		}
2127 		next = STAILQ_NEXT(entry, font_next);
2128 		if (next == NULL ||
2129 		    size > next->font_data->width * next->font_data->height) {
2130 			STAILQ_INSERT_AFTER(&fonts, entry, fp, font_next);
2131 			return (true);
2132 		}
2133 		previous = entry;
2134 	}
2135 	return (true);
2136 }
2137 
2138 static int
font_set(struct env_var * ev __unused,int flags __unused,const void * value)2139 font_set(struct env_var *ev __unused, int flags __unused, const void *value)
2140 {
2141 	struct fontlist *fl;
2142 	char *eptr;
2143 	unsigned long x = 0, y = 0;
2144 
2145 	/*
2146 	 * Attempt to extract values from "XxY" string. In case of error,
2147 	 * we have unmaching glyph dimensions and will just output the
2148 	 * available values.
2149 	 */
2150 	if (value != NULL) {
2151 		x = strtoul(value, &eptr, 10);
2152 		if (*eptr == 'x')
2153 			y = strtoul(eptr + 1, &eptr, 10);
2154 	}
2155 	STAILQ_FOREACH(fl, &fonts, font_next) {
2156 		if (fl->font_data->width == x && fl->font_data->height == y)
2157 			break;
2158 	}
2159 	if (fl != NULL) {
2160 		/* Reset any FONT_MANUAL flag. */
2161 		reset_font_flags();
2162 
2163 		/* Mark this font manually loaded */
2164 		fl->font_flags = FONT_MANUAL;
2165 		/* Trigger tem update. */
2166 		tems.update_font = true;
2167 		plat_cons_update_mode(-1);
2168 		return (CMD_OK);
2169 	}
2170 
2171 	printf("Available fonts:\n");
2172 	STAILQ_FOREACH(fl, &fonts, font_next) {
2173 		printf("    %dx%d\n", fl->font_data->width,
2174 		    fl->font_data->height);
2175 	}
2176 	return (CMD_OK);
2177 }
2178 
2179 void
bios_text_font(bool use_vga_font)2180 bios_text_font(bool use_vga_font)
2181 {
2182 	if (use_vga_font)
2183 		(void) insert_font(VGA_8X16_FONT, FONT_MANUAL);
2184 	else
2185 		(void) insert_font(DEFAULT_8X16_FONT, FONT_MANUAL);
2186 	tems.update_font = true;
2187 }
2188 
2189 void
autoload_font(bool bios)2190 autoload_font(bool bios)
2191 {
2192 	struct name_list *nl;
2193 	struct name_entry *np;
2194 
2195 	nl = read_list("/boot/fonts/fonts.dir");
2196 	if (nl == NULL)
2197 		return;
2198 
2199 	while (!SLIST_EMPTY(nl)) {
2200 		np = SLIST_FIRST(nl);
2201 		SLIST_REMOVE_HEAD(nl, n_entry);
2202 		if (insert_font(np->n_name, FONT_AUTO) == false)
2203 			printf("failed to add font: %s\n", np->n_name);
2204 		free(np->n_name);
2205 		free(np);
2206 	}
2207 
2208 	unsetenv("screen-font");
2209 	env_setenv("screen-font", EV_VOLATILE, NULL, font_set, env_nounset);
2210 
2211 	/*
2212 	 * If vga text mode was requested, load vga.font (8x16 bold) font.
2213 	 */
2214 	if (bios) {
2215 		bios_text_font(true);
2216 	}
2217 
2218 	/* Trigger tem update. */
2219 	tems.update_font = true;
2220 	plat_cons_update_mode(-1);
2221 }
2222 
2223 COMMAND_SET(load_font, "loadfont", "load console font from file", command_font);
2224 
2225 static int
command_font(int argc,char * argv[])2226 command_font(int argc, char *argv[])
2227 {
2228 	int i, c, rc = CMD_OK;
2229 	struct fontlist *fl;
2230 	bool list;
2231 
2232 	list = false;
2233 	optind = 1;
2234 	optreset = 1;
2235 	rc = CMD_OK;
2236 
2237 	while ((c = getopt(argc, argv, "l")) != -1) {
2238 		switch (c) {
2239 		case 'l':
2240 			list = true;
2241 			break;
2242 		case '?':
2243 		default:
2244 			return (CMD_ERROR);
2245 		}
2246 	}
2247 
2248 	argc -= optind;
2249 	argv += optind;
2250 
2251 	if (argc > 1 || (list && argc != 0)) {
2252 		printf("Usage: loadfont [-l] | [file.fnt]\n");
2253 		return (CMD_ERROR);
2254 	}
2255 
2256 	if (list) {
2257 		STAILQ_FOREACH(fl, &fonts, font_next) {
2258 			printf("font %s: %dx%d%s\n", fl->font_name,
2259 			    fl->font_data->width,
2260 			    fl->font_data->height,
2261 			    fl->font_data->font == NULL? "" : " loaded");
2262 		}
2263 		return (CMD_OK);
2264 	}
2265 
2266 	if (argc == 1) {
2267 		char *name = argv[0];
2268 
2269 		if (insert_font(name, FONT_MANUAL) == false) {
2270 			printf("loadfont error: failed to load: %s\n", name);
2271 			return (CMD_ERROR);
2272 		}
2273 
2274 		tems.update_font = true;
2275 		plat_cons_update_mode(-1);
2276 		return (CMD_OK);
2277 	}
2278 
2279 	if (argc == 0) {
2280 		/*
2281 		 * Walk entire font list, release any loaded font, and set
2282 		 * autoload flag. The font list does have at least the builtin
2283 		 * default font.
2284 		 */
2285 		STAILQ_FOREACH(fl, &fonts, font_next) {
2286 			if (fl->font_data->font != NULL) {
2287 				/* Note the tem is releasing font bytes */
2288 				for (i = 0; i < VFNT_MAPS; i++)
2289 					free(fl->font_data->font->vf_map[i]);
2290 				free(fl->font_data->font);
2291 				fl->font_data->font = NULL;
2292 				fl->font_data->uncompressed_size = 0;
2293 				fl->font_flags = FONT_AUTO;
2294 			}
2295 		}
2296 		tems.update_font = true;
2297 		plat_cons_update_mode(-1);
2298 	}
2299 	return (rc);
2300 }
2301 
2302 bool
gfx_get_edid_resolution(struct vesa_edid_info * edid,edid_res_list_t * res)2303 gfx_get_edid_resolution(struct vesa_edid_info *edid, edid_res_list_t *res)
2304 {
2305 	struct resolution *rp, *p;
2306 
2307 	/*
2308 	 * Walk detailed timings tables (4).
2309 	 */
2310 	if ((edid->display.supported_features
2311 	    & EDID_FEATURE_PREFERRED_TIMING_MODE) != 0) {
2312 		/* Walk detailed timing descriptors (4) */
2313 		for (int i = 0; i < DET_TIMINGS; i++) {
2314 			/*
2315 			 * Reserved value 0 is not used for display decriptor.
2316 			 */
2317 			if (edid->detailed_timings[i].pixel_clock == 0)
2318 				continue;
2319 			if ((rp = malloc(sizeof (*rp))) == NULL)
2320 				continue;
2321 			rp->width = GET_EDID_INFO_WIDTH(edid, i);
2322 			rp->height = GET_EDID_INFO_HEIGHT(edid, i);
2323 			if (rp->width > 0 && rp->width <= EDID_MAX_PIXELS &&
2324 			    rp->height > 0 && rp->height <= EDID_MAX_LINES)
2325 				TAILQ_INSERT_TAIL(res, rp, next);
2326 			else
2327 				free(rp);
2328 		}
2329 	}
2330 
2331 	/*
2332 	 * Walk standard timings list (8).
2333 	 */
2334 	for (int i = 0; i < STD_TIMINGS; i++) {
2335 		/* Is this field unused? */
2336 		if (edid->standard_timings[i] == 0x0101)
2337 			continue;
2338 
2339 		if ((rp = malloc(sizeof (*rp))) == NULL)
2340 			continue;
2341 
2342 		rp->width = HSIZE(edid->standard_timings[i]);
2343 		switch (RATIO(edid->standard_timings[i])) {
2344 		case RATIO1_1:
2345 			rp->height = HSIZE(edid->standard_timings[i]);
2346 			if (edid->header.version > 1 ||
2347 			    edid->header.revision > 2) {
2348 				rp->height = rp->height * 10 / 16;
2349 			}
2350 			break;
2351 		case RATIO4_3:
2352 			rp->height = HSIZE(edid->standard_timings[i]) * 3 / 4;
2353 			break;
2354 		case RATIO5_4:
2355 			rp->height = HSIZE(edid->standard_timings[i]) * 4 / 5;
2356 			break;
2357 		case RATIO16_9:
2358 			rp->height = HSIZE(edid->standard_timings[i]) * 9 / 16;
2359 			break;
2360 		}
2361 
2362 		/*
2363 		 * Create resolution list in decreasing order, except keep
2364 		 * first entry (preferred timing mode).
2365 		 */
2366 		TAILQ_FOREACH(p, res, next) {
2367 			if (p->width * p->height < rp->width * rp->height) {
2368 				/* Keep preferred mode first */
2369 				if (TAILQ_FIRST(res) == p)
2370 					TAILQ_INSERT_AFTER(res, p, rp, next);
2371 				else
2372 					TAILQ_INSERT_BEFORE(p, rp, next);
2373 				break;
2374 			}
2375 			if (TAILQ_NEXT(p, next) == NULL) {
2376 				TAILQ_INSERT_TAIL(res, rp, next);
2377 				break;
2378 			}
2379 		}
2380 	}
2381 	return (!TAILQ_EMPTY(res));
2382 }
2383