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