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