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