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