]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/i386/libi386/vbe.c
MFV: zlib 1.3
[FreeBSD/FreeBSD.git] / stand / i386 / libi386 / vbe.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca>
5  * All rights reserved.
6  * Copyright 2020 Toomas Soome
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31 #include <stand.h>
32 #include <sys/param.h>
33 #include <machine/psl.h>
34 #include <machine/cpufunc.h>
35 #include <stdbool.h>
36 #include <bootstrap.h>
37 #include <btxv86.h>
38 #include <gfx_fb.h>
39 #include <dev/vt/hw/vga/vt_vga_reg.h>
40 #include "libi386.h"
41 #include "vbe.h"
42
43 /*
44  * VESA BIOS Extensions routines
45  */
46
47 static struct vbeinfoblock *vbe;
48 static struct modeinfoblock *vbe_mode;
49
50 static uint16_t *vbe_mode_list;
51 static size_t vbe_mode_list_size;
52 struct vesa_edid_info *edid_info = NULL;
53
54 /* The default VGA color palette format is 6 bits per primary color. */
55 int palette_format = 6;
56
57 #define VESA_MODE_BASE  0x100
58
59 /*
60  * palette array for 8-bit indexed colors. In this case, cmap does store
61  * index and pe8 does store actual RGB. This is needed because we may
62  * not be able to read palette data from hardware.
63  */
64 struct paletteentry *pe8 = NULL;
65
66 static struct named_resolution {
67         const char *name;
68         const char *alias;
69         unsigned int width;
70         unsigned int height;
71 } resolutions[] = {
72         {
73                 .name = "480p",
74                 .width = 640,
75                 .height = 480,
76         },
77         {
78                 .name = "720p",
79                 .width = 1280,
80                 .height = 720,
81         },
82         {
83                 .name = "1080p",
84                 .width = 1920,
85                 .height = 1080,
86         },
87         {
88                 .name = "2160p",
89                 .alias = "4k",
90                 .width = 3840,
91                 .height = 2160,
92         },
93         {
94                 .name = "5k",
95                 .width = 5120,
96                 .height = 2880,
97         }
98 };
99
100 static bool
101 vbe_resolution_compare(struct named_resolution *res, const char *cmp)
102 {
103
104         if (strcasecmp(res->name, cmp) == 0)
105                 return (true);
106         if (res->alias != NULL && strcasecmp(res->alias, cmp) == 0)
107                 return (true);
108         return (false);
109 }
110
111 static void
112 vbe_get_max_resolution(int *width, int *height)
113 {
114         struct named_resolution *res;
115         char *maxres;
116         char *height_start, *width_start;
117         int idx;
118
119         *width = *height = 0;
120         maxres = getenv("vbe_max_resolution");
121         /* No max_resolution set? Bail out; choose highest resolution */
122         if (maxres == NULL)
123                 return;
124         /* See if it matches one of our known resolutions */
125         for (idx = 0; idx < nitems(resolutions); ++idx) {
126                 res = &resolutions[idx];
127                 if (vbe_resolution_compare(res, maxres)) {
128                         *width = res->width;
129                         *height = res->height;
130                         return;
131                 }
132         }
133         /* Not a known resolution, try to parse it; make a copy we can modify */
134         maxres = strdup(maxres);
135         if (maxres == NULL)
136                 return;
137         height_start = strchr(maxres, 'x');
138         if (height_start == NULL) {
139                 free(maxres);
140                 return;
141         }
142         width_start = maxres;
143         *height_start++ = 0;
144         /* Errors from this will effectively mean "no max" */
145         *width = (int)strtol(width_start, NULL, 0);
146         *height = (int)strtol(height_start, NULL, 0);
147         free(maxres);
148 }
149
150 int
151 vga_get_reg(int reg, int index)
152 {
153         return (inb(reg + index));
154 }
155
156 int
157 vga_get_atr(int reg, int i)
158 {
159         int ret;
160
161         (void) inb(reg + VGA_GEN_INPUT_STAT_1);
162         outb(reg + VGA_AC_WRITE, i);
163         ret = inb(reg + VGA_AC_READ);
164
165         (void) inb(reg + VGA_GEN_INPUT_STAT_1);
166
167         return (ret);
168 }
169
170 void
171 vga_set_atr(int reg, int i, int v)
172 {
173         (void) inb(reg + VGA_GEN_INPUT_STAT_1);
174         outb(reg + VGA_AC_WRITE, i);
175         outb(reg + VGA_AC_WRITE, v);
176
177         (void) inb(reg + VGA_GEN_INPUT_STAT_1);
178 }
179
180 void
181 vga_set_indexed(int reg, int indexreg, int datareg, uint8_t index, uint8_t val)
182 {
183         outb(reg + indexreg, index);
184         outb(reg + datareg, val);
185 }
186
187 int
188 vga_get_indexed(int reg, int indexreg, int datareg, uint8_t index)
189 {
190         outb(reg + indexreg, index);
191         return (inb(reg + datareg));
192 }
193
194 int
195 vga_get_crtc(int reg, int i)
196 {
197         return (vga_get_indexed(reg, VGA_CRTC_ADDRESS, VGA_CRTC_DATA, i));
198 }
199
200 void
201 vga_set_crtc(int reg, int i, int v)
202 {
203         vga_set_indexed(reg, VGA_CRTC_ADDRESS, VGA_CRTC_DATA, i, v);
204 }
205
206 int
207 vga_get_seq(int reg, int i)
208 {
209         return (vga_get_indexed(reg, VGA_SEQ_ADDRESS, VGA_SEQ_DATA, i));
210 }
211
212 void
213 vga_set_seq(int reg, int i, int v)
214 {
215         vga_set_indexed(reg, VGA_SEQ_ADDRESS, VGA_SEQ_DATA, i, v);
216 }
217
218 int
219 vga_get_grc(int reg, int i)
220 {
221         return (vga_get_indexed(reg, VGA_GC_ADDRESS, VGA_GC_DATA, i));
222 }
223
224 void
225 vga_set_grc(int reg, int i, int v)
226 {
227         vga_set_indexed(reg, VGA_GC_ADDRESS, VGA_GC_DATA, i, v);
228 }
229
230 /*
231  * Return true when this controller is VGA compatible.
232  */
233 bool
234 vbe_is_vga(void)
235 {
236         if (vbe == NULL)
237                 return (false);
238
239         return ((vbe->Capabilities & VBE_CAP_NONVGA) == 0);
240 }
241
242 /* Actually assuming mode 3. */
243 void
244 bios_set_text_mode(int mode)
245 {
246         int atr;
247
248         if (vbe->Capabilities & VBE_CAP_DAC8) {
249                 int m;
250
251                 /*
252                  * The mode change should reset the palette format to
253                  * 6 bits, but apparently some systems do fail with 8-bit
254                  * palette, so we switch to 6-bit here.
255                  */
256                 m = 0x0600;
257                 (void) biosvbe_palette_format(&m);
258                 palette_format = m;
259         }
260         v86.ctl = V86_FLAGS;
261         v86.addr = 0x10;
262         v86.eax = mode;                         /* set VGA text mode */
263         v86int();
264         atr = vga_get_atr(VGA_REG_BASE, VGA_AC_MODE_CONTROL);
265         atr &= ~VGA_AC_MC_BI;
266         atr &= ~VGA_AC_MC_ELG;
267         vga_set_atr(VGA_REG_BASE, VGA_AC_MODE_CONTROL, atr);
268
269         gfx_state.tg_mode = mode;
270         gfx_state.tg_fb_type = FB_TEXT;
271         gfx_state.tg_fb.fb_height = TEXT_ROWS;
272         gfx_state.tg_fb.fb_width = TEXT_COLS;
273
274         gfx_state.tg_fb.fb_mask_red = (1 << palette_format) - 1 << 16;
275         gfx_state.tg_fb.fb_mask_green = (1 << palette_format) - 1 << 8;
276         gfx_state.tg_fb.fb_mask_blue = (1 << palette_format) - 1 << 0;
277         gfx_state.tg_ctype = CT_INDEXED;
278         env_setenv("screen.textmode", EV_VOLATILE | EV_NOHOOK, "1", NULL, NULL);
279 }
280
281 /* Function 00h - Return VBE Controller Information */
282 static int
283 biosvbe_info(struct vbeinfoblock *vbep)
284 {
285         struct vbeinfoblock *rvbe;
286         int ret;
287
288         if (vbep == NULL)
289                 return (VBE_FAILED);
290
291         rvbe = bio_alloc(sizeof(*rvbe));
292         if (rvbe == NULL)
293                 return (VBE_FAILED);
294
295         /* Now check if we have vesa. */
296         memset(rvbe, 0, sizeof (*vbe));
297         memcpy(rvbe->VbeSignature, "VBE2", 4);
298
299         v86.ctl = V86_FLAGS;
300         v86.addr = 0x10;
301         v86.eax = 0x4f00;
302         v86.es = VTOPSEG(rvbe);
303         v86.edi = VTOPOFF(rvbe);
304         v86int();
305         ret = v86.eax & 0xffff;
306
307         if (ret != VBE_SUCCESS)
308                 goto done;
309
310         if (memcmp(rvbe->VbeSignature, "VESA", 4) != 0) {
311                 ret = VBE_NOTSUP;
312                 goto done;
313         }
314         bcopy(rvbe, vbep, sizeof(*vbep));
315 done:
316         bio_free(rvbe, sizeof(*rvbe));
317         return (ret);
318 }
319
320 /* Function 01h - Return VBE Mode Information */
321 static int
322 biosvbe_get_mode_info(int mode, struct modeinfoblock *mi)
323 {
324         struct modeinfoblock *rmi;
325         int ret;
326
327         rmi = bio_alloc(sizeof(*rmi));
328         if (rmi == NULL)
329                 return (VBE_FAILED);
330
331         v86.ctl = V86_FLAGS;
332         v86.addr = 0x10;
333         v86.eax = 0x4f01;
334         v86.ecx = mode;
335         v86.es = VTOPSEG(rmi);
336         v86.edi = VTOPOFF(rmi);
337         v86int();
338
339         ret = v86.eax & 0xffff;
340         if (ret != VBE_SUCCESS)
341                 goto done;
342         bcopy(rmi, mi, sizeof(*rmi));
343 done:
344         bio_free(rmi, sizeof(*rmi));
345         return (ret);
346 }
347
348 /* Function 02h - Set VBE Mode */
349 static int
350 biosvbe_set_mode(int mode, struct crtciinfoblock *ci)
351 {
352         int rv;
353
354         if (vbe->Capabilities & VBE_CAP_DAC8) {
355                 int m;
356
357                 /*
358                  * The mode change should reset the palette format to
359                  * 6 bits, but apparently some systems do fail with 8-bit
360                  * palette, so we switch to 6-bit here.
361                  */
362                 m = 0x0600;
363                 if (biosvbe_palette_format(&m) == VBE_SUCCESS)
364                         palette_format = m;
365         }
366         v86.ctl = V86_FLAGS;
367         v86.addr = 0x10;
368         v86.eax = 0x4f02;
369         v86.ebx = mode | 0x4000;        /* set linear FB bit */
370         v86.es = VTOPSEG(ci);
371         v86.edi = VTOPOFF(ci);
372         v86int();
373         rv = v86.eax & 0xffff;
374         if (vbe->Capabilities & VBE_CAP_DAC8) {
375                 int m;
376
377                 /* Switch to 8-bits per primary color. */
378                 m = 0x0800;
379                 if (biosvbe_palette_format(&m) == VBE_SUCCESS)
380                         palette_format = m;
381         }
382         env_setenv("screen.textmode", EV_VOLATILE | EV_NOHOOK, "0", NULL, NULL);
383         return (rv);
384 }
385
386 /* Function 03h - Get VBE Mode */
387 static int
388 biosvbe_get_mode(int *mode)
389 {
390         v86.ctl = V86_FLAGS;
391         v86.addr = 0x10;
392         v86.eax = 0x4f03;
393         v86int();
394         *mode = v86.ebx & 0x3fff;       /* Bits 0-13 */
395         return (v86.eax & 0xffff);
396 }
397
398 /* Function 08h - Set/Get DAC Palette Format */
399 int
400 biosvbe_palette_format(int *format)
401 {
402         v86.ctl = V86_FLAGS;
403         v86.addr = 0x10;
404         v86.eax = 0x4f08;
405         v86.ebx = *format;
406         v86int();
407         *format = (v86.ebx >> 8) & 0xff;
408         return (v86.eax & 0xffff);
409 }
410
411 /* Function 09h - Set/Get Palette Data */
412 static int
413 biosvbe_palette_data(int mode, int reg, struct paletteentry *pe)
414 {
415         v86.ctl = V86_FLAGS;
416         v86.addr = 0x10;
417         v86.eax = 0x4f09;
418         v86.ebx = mode;
419         v86.edx = reg;
420         v86.ecx = 1;
421         v86.es = VTOPSEG(pe);
422         v86.edi = VTOPOFF(pe);
423         v86int();
424         return (v86.eax & 0xffff);
425 }
426
427 /*
428  * Function 15h BL=00h - Report VBE/DDC Capabilities
429  *
430  * int biosvbe_ddc_caps(void)
431  * return: VBE/DDC capabilities
432  */
433 static int
434 biosvbe_ddc_caps(void)
435 {
436         v86.ctl = V86_FLAGS;
437         v86.addr = 0x10;
438         v86.eax = 0x4f15;       /* display identification extensions */
439         v86.ebx = 0;            /* report DDC capabilities */
440         v86.ecx = 0;            /* controller unit number (00h = primary) */
441         v86.es = 0;
442         v86.edi = 0;
443         v86int();
444         if (VBE_ERROR(v86.eax & 0xffff))
445                 return (0);
446         return (v86.ebx & 0xffff);
447 }
448
449 /* Function 11h BL=01h - Flat Panel status */
450 static int
451 biosvbe_ddc_read_flat_panel_info(void *buf)
452 {
453         v86.ctl = V86_FLAGS;
454         v86.addr = 0x10;
455         v86.eax = 0x4f11;       /* Flat Panel Interface extensions */
456         v86.ebx = 1;            /* Return Flat Panel Information */
457         v86.es = VTOPSEG(buf);
458         v86.edi = VTOPOFF(buf);
459         v86int();
460         return (v86.eax & 0xffff);
461 }
462
463 /* Function 15h BL=01h - Read EDID */
464 static int
465 biosvbe_ddc_read_edid(int blockno, void *buf)
466 {
467         v86.ctl = V86_FLAGS;
468         v86.addr = 0x10;
469         v86.eax = 0x4f15;       /* display identification extensions */
470         v86.ebx = 1;            /* read EDID */
471         v86.ecx = 0;            /* controller unit number (00h = primary) */
472         v86.edx = blockno;
473         v86.es = VTOPSEG(buf);
474         v86.edi = VTOPOFF(buf);
475         v86int();
476         return (v86.eax & 0xffff);
477 }
478
479 static int
480 vbe_mode_is_supported(struct modeinfoblock *mi)
481 {
482         if ((mi->ModeAttributes & 0x01) == 0)
483                 return (0);     /* mode not supported by hardware */
484         if ((mi->ModeAttributes & 0x08) == 0)
485                 return (0);     /* linear fb not available */
486         if ((mi->ModeAttributes & 0x10) == 0)
487                 return (0);     /* text mode */
488         if (mi->NumberOfPlanes != 1)
489                 return (0);     /* planar mode not supported */
490         if (mi->MemoryModel != 0x04 /* Packed pixel */ &&
491             mi->MemoryModel != 0x06 /* Direct Color */)
492                 return (0);     /* unsupported pixel format */
493         return (1);
494 }
495
496 static bool
497 vbe_check(void)
498 {
499
500         if (vbe == NULL) {
501                 printf("VBE not available\n");
502                 return (false);
503         }
504         return (true);
505 }
506
507 static int
508 mode_set(struct env_var *ev, int flags __unused, const void *value)
509 {
510         int mode;
511
512         if (strcmp(ev->ev_name, "screen.textmode") == 0) {
513                 unsigned long v;
514                 char *end;
515
516                 if (value == NULL)
517                         return (0);
518                 errno = 0;
519                 v = strtoul(value, &end, 0);
520                 if (errno != 0 || *(char *)value == '\0' || *end != '\0' ||
521                     (v != 0 && v != 1))
522                         return (EINVAL);
523                 env_setenv("screen.textmode", EV_VOLATILE | EV_NOHOOK,
524                     value, NULL, NULL);
525                 if (v == 1) {
526                         reset_font_flags();
527                         bios_text_font(true);
528                         bios_set_text_mode(VGA_TEXT_MODE);
529                         (void) cons_update_mode(false);
530                         return (0);
531                 }
532         } else if (strcmp(ev->ev_name, "vbe_max_resolution") == 0) {
533                 env_setenv("vbe_max_resolution", EV_VOLATILE | EV_NOHOOK,
534                     value, NULL, NULL);
535         } else {
536                 return (EINVAL);
537         }
538
539         mode = vbe_default_mode();
540         if (gfx_state.tg_mode != mode) {
541                 reset_font_flags();
542                 bios_text_font(false);
543                 vbe_set_mode(mode);
544                 cons_update_mode(true);
545         }
546         return (0);
547 }
548
549 static void *
550 vbe_farptr(uint32_t farptr)
551 {
552         return (PTOV((((farptr & 0xffff0000) >> 12) + (farptr & 0xffff))));
553 }
554
555 void
556 vbe_init(void)
557 {
558         uint16_t *p, *ml;
559
560         /* First set FB for text mode. */
561         gfx_state.tg_fb_type = FB_TEXT;
562         gfx_state.tg_fb.fb_height = TEXT_ROWS;
563         gfx_state.tg_fb.fb_width = TEXT_COLS;
564         gfx_state.tg_ctype = CT_INDEXED;
565         gfx_state.tg_mode = 3;
566
567         env_setenv("screen.textmode", EV_VOLATILE, "1", mode_set,
568             env_nounset);
569         env_setenv("vbe_max_resolution", EV_VOLATILE, NULL, mode_set,
570             env_nounset);
571
572         if (vbe == NULL) {
573                 vbe = malloc(sizeof(*vbe));
574                 if (vbe == NULL)
575                         return;
576         }
577
578         if (vbe_mode == NULL) {
579                 vbe_mode = malloc(sizeof(*vbe_mode));
580                 if (vbe_mode == NULL) {
581                         free(vbe);
582                         vbe = NULL;
583                 }
584         }
585
586         if (biosvbe_info(vbe) != VBE_SUCCESS) {
587                 free(vbe);
588                 vbe = NULL;
589                 free(vbe_mode);
590                 vbe_mode = NULL;
591                 return;
592         }
593
594         /*
595          * Copy mode list. We must do this because some systems do
596          * corrupt the provided list (vbox 6.1 is one example).
597          */
598         p = ml = vbe_farptr(vbe->VideoModePtr);
599         while(*p++ != 0xFFFF)
600                 ;
601
602         vbe_mode_list_size = (uintptr_t)p - (uintptr_t)ml;
603
604         /*
605          * Since vbe_init() is used only once at very start of the loader,
606          * we assume malloc will not fail there, but in case it does,
607          * we point vbe_mode_list to memory pointed by VideoModePtr.
608          */
609         vbe_mode_list = malloc(vbe_mode_list_size);
610         if (vbe_mode_list == NULL)
611                 vbe_mode_list = ml;
612         else
613                 bcopy(ml, vbe_mode_list, vbe_mode_list_size);
614
615         /* reset VideoModePtr, to make sure, we only do use vbe_mode_list. */
616         vbe->VideoModePtr = 0;
617
618         /* vbe_set_mode() will set up the rest. */
619 }
620
621 bool
622 vbe_available(void)
623 {
624         return (gfx_state.tg_fb_type == FB_VBE);
625 }
626
627 int
628 vbe_set_palette(const struct paletteentry *entry, size_t slot)
629 {
630         struct paletteentry pe;
631         int mode, ret;
632
633         if (!vbe_check() || (vbe->Capabilities & VBE_CAP_DAC8) == 0)
634                 return (1);
635
636         if (gfx_state.tg_ctype != CT_INDEXED) {
637                 return (1);
638         }
639
640         pe.Blue = entry->Blue;
641         pe.Green = entry->Green;
642         pe.Red = entry->Red;
643         pe.Reserved = entry->Reserved;
644
645         if (vbe->Capabilities & VBE_CAP_SNOW)
646                 mode = 0x80;
647         else
648                 mode = 0;
649
650         ret = biosvbe_palette_data(mode, slot, &pe);
651
652         return (ret == VBE_SUCCESS ? 0 : 1);
653 }
654
655 int
656 vbe_get_mode(void)
657 {
658         return (gfx_state.tg_mode);
659 }
660
661 int
662 vbe_set_mode(int modenum)
663 {
664         struct modeinfoblock mi;
665         int bpp, ret;
666
667         if (!vbe_check())
668                 return (1);
669
670         ret = biosvbe_get_mode_info(modenum, &mi);
671         if (VBE_ERROR(ret)) {
672                 printf("mode 0x%x invalid\n", modenum);
673                 return (1);
674         }
675
676         if (!vbe_mode_is_supported(&mi)) {
677                 printf("mode 0x%x not supported\n", modenum);
678                 return (1);
679         }
680
681         /* calculate bytes per pixel */
682         switch (mi.BitsPerPixel) {
683         case 32:
684         case 24:
685         case 16:
686         case 15:
687         case 8:
688                 break;
689         default:
690                 printf("BitsPerPixel %d is not supported\n", mi.BitsPerPixel);
691                 return (1);
692         }
693
694         ret = biosvbe_set_mode(modenum, NULL);
695         if (VBE_ERROR(ret)) {
696                 printf("mode 0x%x could not be set\n", modenum);
697                 return (1);
698         }
699
700         gfx_state.tg_mode = modenum;
701         gfx_state.tg_fb_type = FB_VBE;
702         /* make sure we have current MI in vbestate */
703         memcpy(vbe_mode, &mi, sizeof (*vbe_mode));
704
705         gfx_state.tg_fb.fb_addr = (uint64_t)mi.PhysBasePtr & 0xffffffff;
706         gfx_state.tg_fb.fb_height = mi.YResolution;
707         gfx_state.tg_fb.fb_width = mi.XResolution;
708         gfx_state.tg_fb.fb_bpp = mi.BitsPerPixel;
709
710         free(gfx_state.tg_shadow_fb);
711         gfx_state.tg_shadow_fb = malloc(mi.YResolution * mi.XResolution *
712             sizeof(struct paletteentry));
713
714         /* Bytes per pixel */
715         bpp = roundup2(mi.BitsPerPixel, NBBY) / NBBY;
716
717         /* vbe_mode_is_supported() excludes the rest */
718         switch (mi.MemoryModel) {
719         case 0x4:
720                 gfx_state.tg_ctype = CT_INDEXED;
721                 break;
722         case 0x6:
723                 gfx_state.tg_ctype = CT_RGB;
724                 break;
725         }
726
727 #define COLOR_MASK(size, pos) (((1 << size) - 1) << pos)
728         if (gfx_state.tg_ctype == CT_INDEXED) {
729                 gfx_state.tg_fb.fb_mask_red = COLOR_MASK(palette_format, 16);
730                 gfx_state.tg_fb.fb_mask_green = COLOR_MASK(palette_format, 8);
731                 gfx_state.tg_fb.fb_mask_blue = COLOR_MASK(palette_format, 0);
732         } else if (vbe->VbeVersion >= 0x300) {
733                 gfx_state.tg_fb.fb_mask_red =
734                     COLOR_MASK(mi.LinRedMaskSize, mi.LinRedFieldPosition);
735                 gfx_state.tg_fb.fb_mask_green =
736                     COLOR_MASK(mi.LinGreenMaskSize, mi.LinGreenFieldPosition);
737                 gfx_state.tg_fb.fb_mask_blue =
738                     COLOR_MASK(mi.LinBlueMaskSize, mi.LinBlueFieldPosition);
739         } else {
740                 gfx_state.tg_fb.fb_mask_red =
741                     COLOR_MASK(mi.RedMaskSize, mi.RedFieldPosition);
742                 gfx_state.tg_fb.fb_mask_green =
743                     COLOR_MASK(mi.GreenMaskSize, mi.GreenFieldPosition);
744                 gfx_state.tg_fb.fb_mask_blue =
745                     COLOR_MASK(mi.BlueMaskSize, mi.BlueFieldPosition);
746         }
747         gfx_state.tg_fb.fb_mask_reserved = ~(gfx_state.tg_fb.fb_mask_red |
748             gfx_state.tg_fb.fb_mask_green |
749             gfx_state.tg_fb.fb_mask_blue);
750
751         if (vbe->VbeVersion >= 0x300)
752                 gfx_state.tg_fb.fb_stride = mi.LinBytesPerScanLine / bpp;
753         else
754                 gfx_state.tg_fb.fb_stride = mi.BytesPerScanLine / bpp;
755
756         gfx_state.tg_fb.fb_size = mi.YResolution * gfx_state.tg_fb.fb_stride *
757             bpp;
758
759         return (0);
760 }
761
762 /*
763  * Verify existence of mode number or find mode by
764  * dimensions. If depth is not given, walk values 32, 24, 16, 8.
765  */
766 static int
767 vbe_find_mode_xydm(int x, int y, int depth, int m)
768 {
769         struct modeinfoblock mi;
770         uint16_t *farptr;
771         uint16_t mode;
772         int idx, nentries, i;
773
774         memset(vbe, 0, sizeof (*vbe));
775         if (biosvbe_info(vbe) != VBE_SUCCESS)
776                 return (0);
777
778         if (m != -1)
779                 i = 8;
780         else if (depth == -1)
781                 i = 32;
782         else
783                 i = depth;
784
785         nentries = vbe_mode_list_size / sizeof(*vbe_mode_list);
786         while (i > 0) {
787                 for (idx = 0; idx < nentries; idx++) {
788                         mode = vbe_mode_list[idx];
789                         if (mode == 0xffff)
790                                 break;
791
792                         if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS) {
793                                 continue;
794                         }
795
796                         /* we only care about linear modes here */
797                         if (vbe_mode_is_supported(&mi) == 0)
798                                 continue;
799
800                         if (m != -1) {
801                                 if (m == mode)
802                                         return (mode);
803                                 else
804                                         continue;
805                         }
806
807                         if (mi.XResolution == x &&
808                             mi.YResolution == y &&
809                             mi.BitsPerPixel == i)
810                                 return (mode);
811                 }
812                 if (depth != -1)
813                         break;
814
815                 i -= 8;
816         }
817
818         return (0);
819 }
820
821 static int
822 vbe_find_mode(char *str)
823 {
824         int x, y, depth;
825
826         if (!gfx_parse_mode_str(str, &x, &y, &depth))
827                 return (0);
828
829         return (vbe_find_mode_xydm(x, y, depth, -1));
830 }
831
832 static void
833 vbe_dump_mode(int modenum, struct modeinfoblock *mi)
834 {
835         printf("0x%x=%dx%dx%d", modenum,
836             mi->XResolution, mi->YResolution, mi->BitsPerPixel);
837 }
838
839 static bool
840 vbe_get_edid(edid_res_list_t *res)
841 {
842         struct vesa_edid_info *edidp;
843         const uint8_t magic[] = EDID_MAGIC;
844         int ddc_caps;
845         bool ret = false;
846
847         if (edid_info != NULL)
848                 return (gfx_get_edid_resolution(edid_info, res));
849
850         ddc_caps = biosvbe_ddc_caps();
851         if (ddc_caps == 0) {
852                 return (ret);
853         }
854
855         edidp = bio_alloc(sizeof(*edidp));
856         if (edidp == NULL)
857                 return (ret);
858         memset(edidp, 0, sizeof(*edidp));
859
860         if (VBE_ERROR(biosvbe_ddc_read_edid(0, edidp)))
861                 goto done;
862
863         if (memcmp(edidp, magic, sizeof(magic)) != 0)
864                 goto done;
865
866         /* Unknown EDID version. */
867         if (edidp->header.version != 1)
868                 goto done;
869
870         ret = gfx_get_edid_resolution(edidp, res);
871         edid_info = malloc(sizeof(*edid_info));
872         if (edid_info != NULL)
873                 memcpy(edid_info, edidp, sizeof (*edid_info));
874 done:
875         bio_free(edidp, sizeof(*edidp));
876         return (ret);
877 }
878
879 static bool
880 vbe_get_flatpanel(uint32_t *pwidth, uint32_t *pheight)
881 {
882         struct vesa_flat_panel_info *fp_info;
883         bool ret = false;
884
885         fp_info = bio_alloc(sizeof (*fp_info));
886         if (fp_info == NULL)
887                 return (ret);
888         memset(fp_info, 0, sizeof (*fp_info));
889
890         if (VBE_ERROR(biosvbe_ddc_read_flat_panel_info(fp_info)))
891                 goto done;
892
893         *pwidth = fp_info->HSize;
894         *pheight = fp_info->VSize;
895         ret = true;
896
897 done:
898         bio_free(fp_info, sizeof (*fp_info));
899         return (ret);
900 }
901
902 static void
903 vbe_print_memory(unsigned vmem)
904 {
905         char unit = 'K';
906
907         vmem /= 1024;
908         if (vmem >= 10240000) {
909                 vmem /= 1048576;
910                 unit = 'G';
911         } else if (vmem >= 10000) {
912                 vmem /= 1024;
913                 unit = 'M';
914         }
915         printf("Total memory: %u%cB\n", vmem, unit);
916 }
917
918 static void
919 vbe_print_vbe_info(struct vbeinfoblock *vbep)
920 {
921         char *oemstring = "";
922         char *oemvendor = "", *oemproductname = "", *oemproductrev = "";
923
924         if (vbep->OemStringPtr != 0)
925                 oemstring = vbe_farptr(vbep->OemStringPtr);
926
927         if (vbep->OemVendorNamePtr != 0)
928                 oemvendor = vbe_farptr(vbep->OemVendorNamePtr);
929
930         if (vbep->OemProductNamePtr != 0)
931                 oemproductname = vbe_farptr(vbep->OemProductNamePtr);
932
933         if (vbep->OemProductRevPtr != 0)
934                 oemproductrev = vbe_farptr(vbep->OemProductRevPtr);
935
936         printf("VESA VBE Version %d.%d\n%s\n", vbep->VbeVersion >> 8,
937             vbep->VbeVersion & 0xF, oemstring);
938
939         if (vbep->OemSoftwareRev != 0) {
940                 printf("OEM Version %d.%d, %s (%s, %s)\n",
941                     vbep->OemSoftwareRev >> 8, vbep->OemSoftwareRev & 0xF,
942                     oemvendor, oemproductname, oemproductrev);
943         }
944         vbe_print_memory(vbep->TotalMemory << 16);
945         printf("Number of Image Pages: %d\n", vbe_mode->LinNumberOfImagePages);
946 }
947
948 /* List available modes, filter by depth. If depth is -1, list all. */
949 void
950 vbe_modelist(int depth)
951 {
952         struct modeinfoblock mi;
953         uint16_t mode;
954         int nmodes, idx, nentries;
955         int ddc_caps;
956         uint32_t width, height;
957         bool edid = false;
958         edid_res_list_t res;
959         struct resolution *rp;
960
961         if (!vbe_check())
962                 return;
963
964         ddc_caps = biosvbe_ddc_caps();
965         if (ddc_caps & 3) {
966                 printf("DDC");
967                 if (ddc_caps & 1)
968                         printf(" [DDC1]");
969                 if (ddc_caps & 2)
970                         printf(" [DDC2]");
971
972                 TAILQ_INIT(&res);
973                 edid = vbe_get_edid(&res);
974                 if (edid) {
975                         printf(": EDID");
976                         while ((rp = TAILQ_FIRST(&res)) != NULL) {
977                                 printf(" %dx%d", rp->width, rp->height);
978                                 TAILQ_REMOVE(&res, rp, next);
979                                 free(rp);
980                         }
981                         printf("\n");
982                 } else {
983                         printf(": no EDID information\n");
984                 }
985         }
986         if (!edid)
987                 if (vbe_get_flatpanel(&width, &height))
988                         printf(": Panel %dx%d\n", width, height);
989
990         nmodes = 0;
991         memset(vbe, 0, sizeof (*vbe));
992         memcpy(vbe->VbeSignature, "VBE2", 4);
993         if (biosvbe_info(vbe) != VBE_SUCCESS)
994                 goto done;
995         if (memcmp(vbe->VbeSignature, "VESA", 4) != 0)
996                 goto done;
997
998         vbe_print_vbe_info(vbe);
999         printf("Modes: ");
1000
1001         nentries = vbe_mode_list_size / sizeof(*vbe_mode_list);
1002         for (idx = 0; idx < nentries; idx++) {
1003                 mode = vbe_mode_list[idx];
1004                 if (mode == 0xffff)
1005                         break;
1006
1007                 if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS)
1008                         continue;
1009
1010                 /* we only care about linear modes here */
1011                 if (vbe_mode_is_supported(&mi) == 0)
1012                         continue;
1013
1014                 /* apply requested filter */
1015                 if (depth != -1 && mi.BitsPerPixel != depth)
1016                         continue;
1017
1018                 if (nmodes % 4 == 0)
1019                         printf("\n");
1020                 else
1021                         printf("  ");
1022
1023                 vbe_dump_mode(mode, &mi);
1024                 nmodes++;
1025         }
1026
1027 done:
1028         if (nmodes == 0)
1029                 printf("none found");
1030         printf("\n");
1031 }
1032
1033 static void
1034 vbe_print_mode(bool verbose __unused)
1035 {
1036         int nc, mode, i, rc;
1037
1038         nc = NCOLORS;
1039
1040         memset(vbe, 0, sizeof (*vbe));
1041         if (biosvbe_info(vbe) != VBE_SUCCESS)
1042                 return;
1043
1044         vbe_print_vbe_info(vbe);
1045
1046         if (biosvbe_get_mode(&mode) != VBE_SUCCESS) {
1047                 printf("Error getting current VBE mode\n");
1048                 return;
1049         }
1050
1051         if (biosvbe_get_mode_info(mode, vbe_mode) != VBE_SUCCESS ||
1052             vbe_mode_is_supported(vbe_mode) == 0) {
1053                 printf("VBE mode (0x%x) is not framebuffer mode\n", mode);
1054                 return;
1055         }
1056
1057         printf("\nCurrent VBE mode: ");
1058         vbe_dump_mode(mode, vbe_mode);
1059         printf("\n");
1060
1061         printf("%ux%ux%u, stride=%u\n",
1062             gfx_state.tg_fb.fb_width,
1063             gfx_state.tg_fb.fb_height,
1064             gfx_state.tg_fb.fb_bpp,
1065             gfx_state.tg_fb.fb_stride *
1066             (roundup2(gfx_state.tg_fb.fb_bpp, NBBY) / NBBY));
1067         printf("    frame buffer: address=%jx, size=%jx\n",
1068             (uintmax_t)gfx_state.tg_fb.fb_addr,
1069             (uintmax_t)gfx_state.tg_fb.fb_size);
1070
1071         if (vbe_mode->MemoryModel == 0x6) {
1072                 printf("    color mask: R=%08x, G=%08x, B=%08x\n",
1073                     gfx_state.tg_fb.fb_mask_red,
1074                     gfx_state.tg_fb.fb_mask_green,
1075                     gfx_state.tg_fb.fb_mask_blue);
1076                 pager_open();
1077                 for (i = 0; i < nc; i++) {
1078                         printf("%d: R=%02x, G=%02x, B=%02x %08x", i,
1079                             (cmap[i] & gfx_state.tg_fb.fb_mask_red) >>
1080                             ffs(gfx_state.tg_fb.fb_mask_red) - 1,
1081                             (cmap[i] & gfx_state.tg_fb.fb_mask_green) >>
1082                             ffs(gfx_state.tg_fb.fb_mask_green) - 1,
1083                             (cmap[i] & gfx_state.tg_fb.fb_mask_blue) >>
1084                             ffs(gfx_state.tg_fb.fb_mask_blue) - 1, cmap[i]);
1085                         if (pager_output("\n") != 0)
1086                                 break;
1087                 }
1088                 pager_close();
1089                 return;
1090         }
1091
1092         mode = 1;       /* get DAC palette width */
1093         rc = biosvbe_palette_format(&mode);
1094         if (rc != VBE_SUCCESS)
1095                 return;
1096
1097         printf("    palette format: %x bits per primary\n", mode);
1098         if (pe8 == NULL)
1099                 return;
1100
1101         pager_open();
1102         for (i = 0; i < nc; i++) {
1103                 printf("%d: R=%02x, G=%02x, B=%02x", i,
1104                     pe8[i].Red, pe8[i].Green, pe8[i].Blue);
1105                 if (pager_output("\n") != 0)
1106                         break;
1107         }
1108         pager_close();
1109 }
1110
1111 /*
1112  * Try EDID preferred mode, if EDID or the suggested mode is not available,
1113  * then try flat panel information.
1114  * Fall back to VBE_DEFAULT_MODE.
1115  */
1116 int
1117 vbe_default_mode(void)
1118 {
1119         edid_res_list_t res;
1120         struct resolution *rp;
1121         int modenum;
1122         uint32_t width, height;
1123
1124         modenum = 0;
1125         vbe_get_max_resolution(&width, &height);
1126         if (width != 0 && height != 0)
1127                 modenum = vbe_find_mode_xydm(width, height, -1, -1);
1128
1129         TAILQ_INIT(&res);
1130         if (vbe_get_edid(&res)) {
1131                 while ((rp = TAILQ_FIRST(&res)) != NULL) {
1132                         if (modenum == 0) {
1133                                 modenum = vbe_find_mode_xydm(
1134                                     rp->width, rp->height, -1, -1);
1135                         }
1136                         TAILQ_REMOVE(&res, rp, next);
1137                         free(rp);
1138                 }
1139         }
1140
1141         if (modenum == 0 &&
1142             vbe_get_flatpanel(&width, &height)) {
1143                 modenum = vbe_find_mode_xydm(width, height, -1, -1);
1144         }
1145
1146         /* Still no mode? Fall back to default. */
1147         if (modenum == 0)
1148                 modenum = vbe_find_mode(VBE_DEFAULT_MODE);
1149         return (modenum);
1150 }
1151
1152 COMMAND_SET(vbe, "vbe", "vesa framebuffer mode management", command_vesa);
1153
1154 int
1155 command_vesa(int argc, char *argv[])
1156 {
1157         char *arg, *cp;
1158         int modenum = -1, n;
1159
1160         if (!vbe_check())
1161                 return (CMD_OK);
1162
1163         if (argc < 2)
1164                 goto usage;
1165
1166         if (strcmp(argv[1], "list") == 0) {
1167                 n = -1;
1168                 if (argc != 2 && argc != 3)
1169                         goto usage;
1170
1171                 if (argc == 3) {
1172                         arg = argv[2];
1173                         errno = 0;
1174                         n = strtoul(arg, &cp, 0);
1175                         if (errno != 0 || *arg == '\0' || cp[0] != '\0') {
1176                                 snprintf(command_errbuf,
1177                                     sizeof (command_errbuf),
1178                                     "depth should be an integer");
1179                                 return (CMD_ERROR);
1180                         }
1181                 }
1182                 vbe_modelist(n);
1183                 return (CMD_OK);
1184         }
1185
1186         if (strcmp(argv[1], "get") == 0) {
1187                 bool verbose = false;
1188
1189                 if (argc != 2) {
1190                         if (argc > 3 || strcmp(argv[2], "-v") != 0)
1191                                 goto usage;
1192                         verbose = true;
1193                 }
1194                 vbe_print_mode(verbose);
1195                 return (CMD_OK);
1196         }
1197
1198         if (strcmp(argv[1], "off") == 0) {
1199                 if (argc != 2)
1200                         goto usage;
1201
1202                 if (gfx_state.tg_mode == VGA_TEXT_MODE)
1203                         return (CMD_OK);
1204
1205                 reset_font_flags();
1206                 bios_text_font(true);
1207                 bios_set_text_mode(VGA_TEXT_MODE);
1208                 cons_update_mode(false);
1209                 return (CMD_OK);
1210         }
1211
1212         if (strcmp(argv[1], "on") == 0) {
1213                 if (argc != 2)
1214                         goto usage;
1215
1216                 modenum = vbe_default_mode();
1217                 if (modenum == 0) {
1218                         snprintf(command_errbuf, sizeof (command_errbuf),
1219                             "%s: no suitable VBE mode number found", argv[0]);
1220                         return (CMD_ERROR);
1221                 }
1222         } else if (strcmp(argv[1], "set") == 0) {
1223                 if (argc != 3)
1224                         goto usage;
1225
1226                 if (strncmp(argv[2], "0x", 2) == 0) {
1227                         arg = argv[2];
1228                         errno = 0;
1229                         n = strtoul(arg, &cp, 0);
1230                         if (errno != 0 || *arg == '\0' || cp[0] != '\0') {
1231                                 snprintf(command_errbuf,
1232                                     sizeof (command_errbuf),
1233                                     "mode should be an integer");
1234                                 return (CMD_ERROR);
1235                         }
1236                         modenum = vbe_find_mode_xydm(0, 0, 0, n);
1237                 } else if (strchr(argv[2], 'x') != NULL) {
1238                         modenum = vbe_find_mode(argv[2]);
1239                 }
1240         } else {
1241                 goto usage;
1242         }
1243
1244         if (modenum == 0) {
1245                 snprintf(command_errbuf, sizeof (command_errbuf),
1246                     "%s: mode %s not supported by firmware\n",
1247                     argv[0], argv[2]);
1248                 return (CMD_ERROR);
1249         }
1250
1251         if (modenum >= VESA_MODE_BASE) {
1252                 if (gfx_state.tg_mode != modenum) {
1253                         reset_font_flags();
1254                         bios_text_font(false);
1255                         vbe_set_mode(modenum);
1256                         cons_update_mode(true);
1257                 }
1258                 return (CMD_OK);
1259         } else {
1260                 snprintf(command_errbuf, sizeof (command_errbuf),
1261                     "%s: mode %s is not framebuffer mode\n", argv[0], argv[2]);
1262                 return (CMD_ERROR);
1263         }
1264
1265 usage:
1266         snprintf(command_errbuf, sizeof (command_errbuf),
1267             "usage: %s on | off | get | list [depth] | "
1268             "set <display or VBE mode number>", argv[0]);
1269         return (CMD_ERROR);
1270 }