1 /*
2 * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
15 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
16 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 /*
28 * VESA BIOS Extensions routines
29 */
30
31 #include <stand.h>
32 #include <stdbool.h>
33 #include <bootstrap.h>
34 #include <machine/bootinfo.h>
35 #include <machine/metadata.h>
36 #include <sys/multiboot2.h>
37 #include <btxv86.h>
38 #include "libi386.h"
39 #include "gfx_fb.h" /* for EDID */
40 #include "vbe.h"
41 #include <sys/font.h>
42 #include <sys/rgb.h>
43 #include <sys/vgareg.h>
44 #include <sys/vgasubr.h>
45
46 multiboot_tag_vbe_t vbestate;
47 static struct vbeinfoblock *vbe =
48 (struct vbeinfoblock *)&vbestate.vbe_control_info;
49 static struct modeinfoblock *vbe_mode =
50 (struct modeinfoblock *)&vbestate.vbe_mode_info;
51 static uint16_t *vbe_mode_list;
52 static size_t vbe_mode_list_size;
53 struct vesa_edid_info *edid_info = NULL;
54 multiboot_color_t *cmap;
55 /* The default VGA color palette format is 6 bits per primary color. */
56 int palette_format = 6;
57
58 #define VESA_MODE_BASE 0x100
59 #define VESA_END_OF_MODE_LIST 0xffff
60
61 /* Actually assuming mode 3. */
62 void
bios_set_text_mode(int mode)63 bios_set_text_mode(int mode)
64 {
65 int atr;
66
67 if (vbe->Capabilities & VBE_CAP_DAC8) {
68 int m;
69
70 /*
71 * The mode change should reset the palette format to
72 * 6 bits, but apparently some systems do fail with 8-bit
73 * palette, so we switch to 6-bit here.
74 */
75 m = 0x0600;
76 (void) biosvbe_palette_format(&m);
77 palette_format = m;
78 }
79 v86.ctl = V86_FLAGS;
80 v86.addr = 0x10;
81 v86.eax = mode; /* set VGA text mode */
82 v86int();
83 atr = vga_get_atr(VGA_REG_ADDR, VGA_ATR_MODE);
84 atr &= ~VGA_ATR_MODE_BLINK;
85 atr &= ~VGA_ATR_MODE_9WIDE;
86 vga_set_atr(VGA_REG_ADDR, VGA_ATR_MODE, atr);
87
88 vbestate.vbe_mode = 0; /* vbe is disabled */
89 gfx_fb.framebuffer_common.framebuffer_type =
90 MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT;
91 /* 16 bits per character */
92 gfx_fb.framebuffer_common.framebuffer_bpp = 16;
93 gfx_fb.framebuffer_common.framebuffer_addr =
94 VGA_MEM_ADDR + VGA_COLOR_BASE;
95 gfx_fb.framebuffer_common.framebuffer_width = TEXT_COLS;
96 gfx_fb.framebuffer_common.framebuffer_height = TEXT_ROWS;
97 gfx_fb.framebuffer_common.framebuffer_pitch = TEXT_COLS * 2;
98 }
99
100 /* Function 00h - Return VBE Controller Information */
101 static int
biosvbe_info(struct vbeinfoblock * vbe)102 biosvbe_info(struct vbeinfoblock *vbe)
103 {
104 v86.ctl = V86_FLAGS;
105 v86.addr = 0x10;
106 v86.eax = 0x4f00;
107 v86.es = VTOPSEG(vbe);
108 v86.edi = VTOPOFF(vbe);
109 v86int();
110 return (v86.eax & 0xffff);
111 }
112
113 /* Function 01h - Return VBE Mode Information */
114 static int
biosvbe_get_mode_info(int mode,struct modeinfoblock * mi)115 biosvbe_get_mode_info(int mode, struct modeinfoblock *mi)
116 {
117 v86.ctl = V86_FLAGS;
118 v86.addr = 0x10;
119 v86.eax = 0x4f01;
120 v86.ecx = mode;
121 v86.es = VTOPSEG(mi);
122 v86.edi = VTOPOFF(mi);
123 v86int();
124 return (v86.eax & 0xffff);
125 }
126
127 /* Function 02h - Set VBE Mode */
128 static int
biosvbe_set_mode(int mode,struct crtciinfoblock * ci)129 biosvbe_set_mode(int mode, struct crtciinfoblock *ci)
130 {
131 int rv;
132
133 if (vbe->Capabilities & VBE_CAP_DAC8) {
134 int m;
135
136 /*
137 * The mode change should reset the palette format to
138 * 6 bits, but apparently some systems do fail with 8-bit
139 * palette, so we switch to 6-bit here.
140 */
141 m = 0x0600;
142 if (biosvbe_palette_format(&m) == VBE_SUCCESS)
143 palette_format = m;
144 }
145 v86.ctl = V86_FLAGS;
146 v86.addr = 0x10;
147 v86.eax = 0x4f02;
148 v86.ebx = mode | 0x4000; /* set linear FB bit */
149 v86.es = VTOPSEG(ci);
150 v86.edi = VTOPOFF(ci);
151 v86int();
152 rv = v86.eax & 0xffff;
153 if (vbe->Capabilities & VBE_CAP_DAC8) {
154 int m;
155
156 /* Switch to 8-bits per primary color. */
157 m = 0x0800;
158 if (biosvbe_palette_format(&m) == VBE_SUCCESS)
159 palette_format = m;
160 }
161 return (rv);
162 }
163
164 /* Function 03h - Get VBE Mode */
165 static int
biosvbe_get_mode(int * mode)166 biosvbe_get_mode(int *mode)
167 {
168 v86.ctl = V86_FLAGS;
169 v86.addr = 0x10;
170 v86.eax = 0x4f03;
171 v86int();
172 *mode = v86.ebx & 0x3fff; /* Bits 0-13 */
173 return (v86.eax & 0xffff);
174 }
175
176 /* Function 08h - Set/Get DAC Palette Format */
177 int
biosvbe_palette_format(int * format)178 biosvbe_palette_format(int *format)
179 {
180 v86.ctl = V86_FLAGS;
181 v86.addr = 0x10;
182 v86.eax = 0x4f08;
183 v86.ebx = *format;
184 v86int();
185 *format = (v86.ebx >> 8) & 0xff;
186 return (v86.eax & 0xffff);
187 }
188
189 /* Function 09h - Set/Get Palette Data */
190 static int
biosvbe_palette_data(int mode,int reg,struct paletteentry * pe)191 biosvbe_palette_data(int mode, int reg, struct paletteentry *pe)
192 {
193 v86.ctl = V86_FLAGS;
194 v86.addr = 0x10;
195 v86.eax = 0x4f09;
196 v86.ebx = mode;
197 v86.edx = reg;
198 v86.ecx = 1;
199 v86.es = VTOPSEG(pe);
200 v86.edi = VTOPOFF(pe);
201 v86int();
202 return (v86.eax & 0xffff);
203 }
204
205 /*
206 * Function 15h BL=00h - Report VBE/DDC Capabilities
207 *
208 * int biosvbe_ddc_caps(void)
209 * return: VBE/DDC capabilities
210 */
211 static int
biosvbe_ddc_caps(void)212 biosvbe_ddc_caps(void)
213 {
214 v86.ctl = V86_FLAGS;
215 v86.addr = 0x10;
216 v86.eax = 0x4f15; /* display identification extensions */
217 v86.ebx = 0; /* report DDC capabilities */
218 v86.ecx = 0; /* controller unit number (00h = primary) */
219 v86.es = 0;
220 v86.edi = 0;
221 v86int();
222 if (VBE_ERROR(v86.eax & 0xffff))
223 return (0);
224 return (v86.ebx & 0xffff);
225 }
226
227 /* Function 11h BL=01h - Flat Panel status */
228 static int
biosvbe_ddc_read_flat_panel_info(void * buf)229 biosvbe_ddc_read_flat_panel_info(void *buf)
230 {
231 v86.ctl = V86_FLAGS;
232 v86.addr = 0x10;
233 v86.eax = 0x4f11; /* Flat Panel Interface extensions */
234 v86.ebx = 1; /* Return Flat Panel Information */
235 v86.es = VTOPSEG(buf);
236 v86.edi = VTOPOFF(buf);
237 v86int();
238 return (v86.eax & 0xffff);
239 }
240
241 /* Function 15h BL=01h - Read EDID */
242 static int
biosvbe_ddc_read_edid(int blockno,void * buf)243 biosvbe_ddc_read_edid(int blockno, void *buf)
244 {
245 v86.ctl = V86_FLAGS;
246 v86.addr = 0x10;
247 v86.eax = 0x4f15; /* display identification extensions */
248 v86.ebx = 1; /* read EDID */
249 v86.ecx = 0; /* controller unit number (00h = primary) */
250 v86.edx = blockno;
251 v86.es = VTOPSEG(buf);
252 v86.edi = VTOPOFF(buf);
253 v86int();
254 return (v86.eax & 0xffff);
255 }
256
257 static int
vbe_mode_is_supported(struct modeinfoblock * mi)258 vbe_mode_is_supported(struct modeinfoblock *mi)
259 {
260 if ((mi->ModeAttributes & 0x01) == 0)
261 return (0); /* mode not supported by hardware */
262 if ((mi->ModeAttributes & 0x08) == 0)
263 return (0); /* linear fb not available */
264 if ((mi->ModeAttributes & 0x10) == 0)
265 return (0); /* text mode */
266 if (mi->NumberOfPlanes != 1)
267 return (0); /* planar mode not supported */
268 if (mi->MemoryModel != 0x04 /* Packed pixel */ &&
269 mi->MemoryModel != 0x06 /* Direct Color */)
270 return (0); /* unsupported pixel format */
271 return (1);
272 }
273
274 static int
vbe_check(void)275 vbe_check(void)
276 {
277 if (vbestate.mb_type != MULTIBOOT_TAG_TYPE_VBE) {
278 printf("VBE not available\n");
279 return (0);
280 }
281 return (1);
282 }
283
284 /*
285 * Translate selector:offset style address to linear adress.
286 * selector = farptr >> 16;
287 * offset = farptr & 0xffff;
288 * linear = (selector * 4) + offset.
289 * By using mask 0xffff0000, we wil get the optimised line below.
290 * As a final step, translate physical address to loader virtual address.
291 */
292 static void *
vbe_farptr(uint32_t farptr)293 vbe_farptr(uint32_t farptr)
294 {
295 return (PTOV((((farptr & 0xffff0000) >> 12) + (farptr & 0xffff))));
296 }
297
298 void
vbe_init(void)299 vbe_init(void)
300 {
301 uint16_t *p, *ml;
302
303 /* First set FB for text mode. */
304 gfx_fb.framebuffer_common.mb_type = MULTIBOOT_TAG_TYPE_FRAMEBUFFER;
305 gfx_fb.framebuffer_common.framebuffer_type =
306 MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT;
307 /* 16 bits per character */
308 gfx_fb.framebuffer_common.framebuffer_bpp = 16;
309 gfx_fb.framebuffer_common.framebuffer_addr =
310 VGA_MEM_ADDR + VGA_COLOR_BASE;
311 gfx_fb.framebuffer_common.framebuffer_width = TEXT_COLS;
312 gfx_fb.framebuffer_common.framebuffer_height = TEXT_ROWS;
313 gfx_fb.framebuffer_common.framebuffer_pitch = TEXT_COLS * 2;
314
315 /* Now check if we have vesa. */
316 memset(vbe, 0, sizeof (*vbe));
317 memcpy(vbe->VbeSignature, "VBE2", 4);
318 if (biosvbe_info(vbe) != VBE_SUCCESS)
319 return;
320 if (memcmp(vbe->VbeSignature, "VESA", 4) != 0)
321 return;
322
323 /*
324 * Copy mode list array. We must do this because some systems do
325 * place this array to scratch memory, which will be reused by
326 * subsequent VBE calls. (vbox 6.1 is one example).
327 */
328 p = ml = vbe_farptr(vbe->VideoModePtr);
329 while (*p++ != VESA_END_OF_MODE_LIST)
330 ;
331
332 vbe_mode_list_size = (uintptr_t)p - (uintptr_t)ml;
333
334 /*
335 * Since vbe_init() is used only once at very start of the loader,
336 * we assume malloc will not fail there. But in case it does,
337 * we point vbe_mode_list to memory pointed by VideoModePtr.
338 * If the VideoModePtr memory area is not valid, we will fail to
339 * pick usable VBE mode and fall back to use text mode.
340 */
341 vbe_mode_list = malloc(vbe_mode_list_size);
342 if (vbe_mode_list == NULL)
343 vbe_mode_list = ml;
344 else
345 bcopy(ml, vbe_mode_list, vbe_mode_list_size);
346
347 /* reset VideoModePtr, to make sure, we only do use vbe_mode_list. */
348 vbe->VideoModePtr = 0;
349
350 vbestate.mb_type = MULTIBOOT_TAG_TYPE_VBE;
351 vbestate.mb_size = sizeof (vbestate);
352 vbestate.vbe_mode = 0;
353
354 /* vbe_set_mode() will set up the rest. */
355 }
356
357 int
vbe_available(void)358 vbe_available(void)
359 {
360 return (vbestate.mb_type);
361 }
362
363 int
vbe_set_palette(const struct paletteentry * entry,size_t slot)364 vbe_set_palette(const struct paletteentry *entry, size_t slot)
365 {
366 struct paletteentry pe;
367 int mode, ret;
368
369 if (!vbe_check() || (vbe->Capabilities & VBE_CAP_DAC8) == 0)
370 return (1);
371
372 if (gfx_fb.framebuffer_common.framebuffer_type !=
373 MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED) {
374 return (1);
375 }
376
377 if (cmap == NULL)
378 cmap = calloc(CMAP_SIZE, sizeof (*cmap));
379
380 pe.Blue = entry->Blue;
381 pe.Green = entry->Green;
382 pe.Red = entry->Red;
383 pe.Reserved = entry->Reserved;
384
385 if (vbe->Capabilities & VBE_CAP_SNOW)
386 mode = 0x80;
387 else
388 mode = 0;
389
390 ret = biosvbe_palette_data(mode, slot, &pe);
391 if (cmap != NULL && slot < CMAP_SIZE) {
392 cmap[slot].mb_red = entry->Red;
393 cmap[slot].mb_green = entry->Green;
394 cmap[slot].mb_blue = entry->Blue;
395 }
396
397 return (ret == VBE_SUCCESS ? 0 : 1);
398 }
399
400 int
vbe_get_mode(void)401 vbe_get_mode(void)
402 {
403 return (vbestate.vbe_mode);
404 }
405
406 int
vbe_set_mode(int modenum)407 vbe_set_mode(int modenum)
408 {
409 extern struct paletteentry *shadow_fb;
410 struct modeinfoblock mi;
411 int ret;
412
413 if (!vbe_check())
414 return (1);
415
416 ret = biosvbe_get_mode_info(modenum, &mi);
417 if (VBE_ERROR(ret)) {
418 printf("mode 0x%x invalid\n", modenum);
419 return (1);
420 }
421
422 if (!vbe_mode_is_supported(&mi)) {
423 printf("mode 0x%x not supported\n", modenum);
424 return (1);
425 }
426
427 /* calculate bytes per pixel */
428 switch (mi.BitsPerPixel) {
429 case 32:
430 case 24:
431 case 16:
432 case 15:
433 case 8:
434 break;
435 default:
436 printf("BitsPerPixel %d is not supported\n", mi.BitsPerPixel);
437 return (1);
438 }
439
440 ret = biosvbe_set_mode(modenum, NULL);
441 if (VBE_ERROR(ret)) {
442 printf("mode 0x%x could not be set\n", modenum);
443 return (1);
444 }
445
446 /* make sure we have current MI in vbestate */
447 memcpy(vbe_mode, &mi, sizeof (*vbe_mode));
448 vbestate.vbe_mode = modenum;
449
450 if (shadow_fb != NULL)
451 free(shadow_fb);
452 shadow_fb = malloc(mi.XResolution * mi.YResolution *
453 sizeof (*shadow_fb));
454
455 gfx_fb.framebuffer_common.framebuffer_addr =
456 (uint64_t)mi.PhysBasePtr & 0xffffffff;
457 gfx_fb.framebuffer_common.framebuffer_width = mi.XResolution;
458 gfx_fb.framebuffer_common.framebuffer_height = mi.YResolution;
459 gfx_fb.framebuffer_common.framebuffer_bpp = mi.BitsPerPixel;
460
461 /* vbe_mode_is_supported() excludes the rest */
462 switch (mi.MemoryModel) {
463 case 0x4:
464 gfx_fb.framebuffer_common.framebuffer_type =
465 MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED;
466 break;
467 case 0x6:
468 gfx_fb.framebuffer_common.framebuffer_type =
469 MULTIBOOT_FRAMEBUFFER_TYPE_RGB;
470 break;
471 }
472
473 if (vbe->VbeVersion >= 0x300) {
474 gfx_fb.framebuffer_common.framebuffer_pitch =
475 mi.LinBytesPerScanLine;
476 gfx_fb.u.fb2.framebuffer_red_field_position =
477 mi.LinRedFieldPosition;
478 gfx_fb.u.fb2.framebuffer_red_mask_size = mi.LinRedMaskSize;
479 gfx_fb.u.fb2.framebuffer_green_field_position =
480 mi.LinGreenFieldPosition;
481 gfx_fb.u.fb2.framebuffer_green_mask_size = mi.LinGreenMaskSize;
482 gfx_fb.u.fb2.framebuffer_blue_field_position =
483 mi.LinBlueFieldPosition;
484 gfx_fb.u.fb2.framebuffer_blue_mask_size = mi.LinBlueMaskSize;
485 } else {
486 gfx_fb.framebuffer_common.framebuffer_pitch =
487 mi.BytesPerScanLine;
488 gfx_fb.u.fb2.framebuffer_red_field_position =
489 mi.RedFieldPosition;
490 gfx_fb.u.fb2.framebuffer_red_mask_size = mi.RedMaskSize;
491 gfx_fb.u.fb2.framebuffer_green_field_position =
492 mi.GreenFieldPosition;
493 gfx_fb.u.fb2.framebuffer_green_mask_size = mi.GreenMaskSize;
494 gfx_fb.u.fb2.framebuffer_blue_field_position =
495 mi.BlueFieldPosition;
496 gfx_fb.u.fb2.framebuffer_blue_mask_size = mi.BlueMaskSize;
497 }
498
499 /*
500 * Support for color mapping.
501 * For 8, 24 and 32 bit depth, use mask size 8.
502 * 15/16 bit depth needs to use mask size from mode, or we will
503 * lose color information from 32-bit to 15/16 bit translation.
504 */
505 if (mi.BitsPerPixel == 15 || mi.BitsPerPixel == 16) {
506 rgb_info.red.size = gfx_fb.u.fb2.framebuffer_red_mask_size;
507 rgb_info.green.size = gfx_fb.u.fb2.framebuffer_green_mask_size;
508 rgb_info.blue.size = gfx_fb.u.fb2.framebuffer_blue_mask_size;
509 } else {
510 rgb_info.red.size = 8;
511 rgb_info.green.size = 8;
512 rgb_info.blue.size = 8;
513 }
514 rgb_info.red.pos = 16;
515 rgb_info.green.pos = 8;
516 rgb_info.blue.pos = 0;
517
518 return (0);
519 }
520
521 /*
522 * Verify existance of mode number or find mode by
523 * dimensions. If depth is not given, walk values 32, 24, 16, 8.
524 */
525 static int
vbe_find_mode_xydm(int x,int y,int depth,int m)526 vbe_find_mode_xydm(int x, int y, int depth, int m)
527 {
528 struct modeinfoblock mi;
529 uint16_t mode;
530 size_t idx, nentries;
531 int i;
532
533 memset(vbe, 0, sizeof (vbe));
534 memcpy(vbe->VbeSignature, "VBE2", 4);
535 if (biosvbe_info(vbe) != VBE_SUCCESS)
536 return (0);
537 if (memcmp(vbe->VbeSignature, "VESA", 4) != 0)
538 return (0);
539
540 if (m != -1)
541 i = 8;
542 else if (depth == -1)
543 i = 32;
544 else
545 i = depth;
546
547 nentries = vbe_mode_list_size / sizeof (*vbe_mode_list);
548 while (i > 0) {
549 for (idx = 0; idx < nentries; idx++) {
550 mode = vbe_mode_list[idx];
551 if (mode == VESA_END_OF_MODE_LIST)
552 break;
553
554 if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS)
555 continue;
556
557 /* we only care about linear modes here */
558 if (vbe_mode_is_supported(&mi) == 0)
559 continue;
560
561 if (m != -1) {
562 if (m == mode)
563 return (mode);
564 else
565 continue;
566 }
567
568 if (mi.XResolution == x &&
569 mi.YResolution == y &&
570 mi.BitsPerPixel == i)
571 return (mode);
572 }
573 if (depth != -1)
574 break;
575
576 i -= 8;
577 }
578
579 return (0);
580 }
581
582 static int
vbe_find_mode(char * str)583 vbe_find_mode(char *str)
584 {
585 int x, y, depth;
586
587 if (!gfx_parse_mode_str(str, &x, &y, &depth))
588 return (0);
589
590 return (vbe_find_mode_xydm(x, y, depth, -1));
591 }
592
593 static void
vbe_dump_mode(int modenum,struct modeinfoblock * mi)594 vbe_dump_mode(int modenum, struct modeinfoblock *mi)
595 {
596 printf("0x%x=%dx%dx%d", modenum,
597 mi->XResolution, mi->YResolution, mi->BitsPerPixel);
598 }
599
600 static bool
vbe_get_edid(edid_res_list_t * res)601 vbe_get_edid(edid_res_list_t *res)
602 {
603 struct vesa_edid_info *edidp;
604 const uint8_t magic[] = EDID_MAGIC;
605 int ddc_caps;
606 bool ret = false;
607
608 if (edid_info != NULL)
609 return (gfx_get_edid_resolution(edid_info, res));
610
611 ddc_caps = biosvbe_ddc_caps();
612 if (ddc_caps == 0) {
613 return (ret);
614 }
615
616 edidp = bio_alloc(sizeof (*edidp));
617 if (edidp == NULL)
618 return (ret);
619 memset(edidp, 0, sizeof (*edidp));
620
621 if (VBE_ERROR(biosvbe_ddc_read_edid(0, edidp)))
622 goto done;
623
624 if (memcmp(edidp, magic, sizeof (magic)) != 0)
625 goto done;
626
627 /* Unknown EDID version. */
628 if (edidp->header.version != 1)
629 goto done;
630
631 ret = gfx_get_edid_resolution(edidp, res);
632 edid_info = malloc(sizeof (*edid_info));
633 if (edid_info != NULL)
634 memcpy(edid_info, edidp, sizeof (*edid_info));
635 done:
636 bio_free(edidp, sizeof (*edidp));
637 return (ret);
638 }
639
640 static bool
vbe_get_flatpanel(uint_t * pwidth,uint_t * pheight)641 vbe_get_flatpanel(uint_t *pwidth, uint_t *pheight)
642 {
643 struct flatpanelinfo *fp_info;
644 bool ret = false;
645
646 fp_info = bio_alloc(sizeof (*fp_info));
647 if (fp_info == NULL)
648 return (ret);
649 memset(fp_info, 0, sizeof (*fp_info));
650
651 if (VBE_ERROR(biosvbe_ddc_read_flat_panel_info(fp_info)))
652 goto done;
653
654 *pwidth = fp_info->HorizontalSize;
655 *pheight = fp_info->VerticalSize;
656 ret = true;
657
658 done:
659 bio_free(fp_info, sizeof (*fp_info));
660 return (ret);
661 }
662
663 static void
vbe_print_vbe_info(struct vbeinfoblock * vbep)664 vbe_print_vbe_info(struct vbeinfoblock *vbep)
665 {
666 char *oemstring = "";
667 char *oemvendor = "", *oemproductname = "", *oemproductrev = "";
668
669 if (vbep->OemStringPtr != 0)
670 oemstring = vbe_farptr(vbep->OemStringPtr);
671
672 if (vbep->OemVendorNamePtr != 0)
673 oemvendor = vbe_farptr(vbep->OemVendorNamePtr);
674
675 if (vbep->OemProductNamePtr != 0)
676 oemproductname = vbe_farptr(vbep->OemProductNamePtr);
677
678 if (vbep->OemProductRevPtr != 0)
679 oemproductrev = vbe_farptr(vbep->OemProductRevPtr);
680
681 printf("VESA VBE Version %d.%d\n%s\n", vbep->VbeVersion >> 8,
682 vbep->VbeVersion & 0xF, oemstring);
683
684 if (vbep->OemSoftwareRev != 0) {
685 printf("OEM Version %d.%d, %s (%s, %s)\n",
686 vbep->OemSoftwareRev >> 8, vbep->OemSoftwareRev & 0xF,
687 oemvendor, oemproductname, oemproductrev);
688 }
689 }
690
691 /* List available modes, filter by depth. If depth is -1, list all. */
692 void
vbe_modelist(int depth)693 vbe_modelist(int depth)
694 {
695 struct modeinfoblock mi;
696 uint16_t mode;
697 int nmodes, idx, nentries;
698 int ddc_caps;
699 uint_t width, height;
700 bool edid = false;
701 edid_res_list_t res;
702 struct resolution *rp;
703
704 if (!vbe_check())
705 return;
706
707 ddc_caps = biosvbe_ddc_caps();
708 if (ddc_caps & 3) {
709 printf("DDC");
710 if (ddc_caps & 1)
711 printf(" [DDC1]");
712 if (ddc_caps & 2)
713 printf(" [DDC2]");
714
715 TAILQ_INIT(&res);
716 edid = vbe_get_edid(&res);
717 if (edid) {
718 printf(": EDID");
719 while ((rp = TAILQ_FIRST(&res)) != NULL) {
720 printf(" %dx%d", rp->width, rp->height);
721 TAILQ_REMOVE(&res, rp, next);
722 free(rp);
723 }
724 printf("\n");
725 } else {
726 printf(": no EDID information\n");
727 }
728 }
729 if (!edid)
730 if (vbe_get_flatpanel(&width, &height))
731 printf(": Panel %dx%d\n", width, height);
732
733 nmodes = 0;
734 memset(vbe, 0, sizeof (vbe));
735 memcpy(vbe->VbeSignature, "VBE2", 4);
736 if (biosvbe_info(vbe) != VBE_SUCCESS)
737 goto done;
738 if (memcmp(vbe->VbeSignature, "VESA", 4) != 0)
739 goto done;
740
741 vbe_print_vbe_info(vbe);
742 printf("Modes: ");
743
744 nentries = vbe_mode_list_size / sizeof (*vbe_mode_list);
745 for (idx = 0; idx < nentries; idx++) {
746 mode = vbe_mode_list[idx];
747 if (mode == VESA_END_OF_MODE_LIST)
748 break;
749
750 if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS)
751 continue;
752
753 /* we only care about linear modes here */
754 if (vbe_mode_is_supported(&mi) == 0)
755 continue;
756
757 /* apply requested filter */
758 if (depth != -1 && mi.BitsPerPixel != depth)
759 continue;
760
761 if (nmodes % 4 == 0)
762 printf("\n");
763 else
764 printf(" ");
765
766 vbe_dump_mode(mode, &mi);
767 nmodes++;
768 }
769
770 done:
771 if (nmodes == 0)
772 printf("none found");
773 printf("\n");
774 }
775
776 static void
vbe_print_mode(bool verbose)777 vbe_print_mode(bool verbose)
778 {
779 int nc, mode, i, rc;
780
781 if (verbose)
782 nc = 256;
783 else
784 nc = 16;
785
786 memset(vbe, 0, sizeof (vbe));
787 memcpy(vbe->VbeSignature, "VBE2", 4);
788 if (biosvbe_info(vbe) != VBE_SUCCESS)
789 return;
790
791 if (memcmp(vbe->VbeSignature, "VESA", 4) != 0)
792 return;
793
794 vbe_print_vbe_info(vbe);
795
796 if (biosvbe_get_mode(&mode) != VBE_SUCCESS) {
797 printf("Error getting current VBE mode\n");
798 return;
799 }
800
801 if (biosvbe_get_mode_info(mode, vbe_mode) != VBE_SUCCESS ||
802 vbe_mode_is_supported(vbe_mode) == 0) {
803 printf("VBE mode (0x%x) is not framebuffer mode\n", mode);
804 return;
805 }
806
807 printf("\nCurrent VBE mode: ");
808 vbe_dump_mode(mode, vbe_mode);
809 printf("\n");
810
811 printf("%ux%ux%u, stride=%u\n",
812 gfx_fb.framebuffer_common.framebuffer_width,
813 gfx_fb.framebuffer_common.framebuffer_height,
814 gfx_fb.framebuffer_common.framebuffer_bpp,
815 (gfx_fb.framebuffer_common.framebuffer_pitch << 3) /
816 gfx_fb.framebuffer_common.framebuffer_bpp);
817 printf(" frame buffer: address=%jx, size=%jx\n",
818 (uintmax_t)gfx_fb.framebuffer_common.framebuffer_addr,
819 (uintmax_t)gfx_fb.framebuffer_common.framebuffer_height *
820 gfx_fb.framebuffer_common.framebuffer_pitch);
821
822 if (vbe_mode->MemoryModel == 0x6) {
823 printf(" color mask: R=%08x, G=%08x, B=%08x\n",
824 ((1 << gfx_fb.u.fb2.framebuffer_red_mask_size) - 1) <<
825 gfx_fb.u.fb2.framebuffer_red_field_position,
826 ((1 << gfx_fb.u.fb2.framebuffer_green_mask_size) - 1) <<
827 gfx_fb.u.fb2.framebuffer_green_field_position,
828 ((1 << gfx_fb.u.fb2.framebuffer_blue_mask_size) - 1) <<
829 gfx_fb.u.fb2.framebuffer_blue_field_position);
830 return;
831 }
832
833 mode = 1; /* get DAC palette width */
834 rc = biosvbe_palette_format(&mode);
835 if (rc != VBE_SUCCESS)
836 return;
837
838 printf(" palette format: %x bits per primary\n", mode);
839 if (cmap == NULL)
840 return;
841
842 pager_open();
843 for (i = 0; i < nc; i++) {
844 printf("%d: R=%02x, G=%02x, B=%02x", i,
845 cmap[i].mb_red, cmap[i].mb_green, cmap[i].mb_blue);
846 if (pager_output("\n") != 0)
847 break;
848 }
849 pager_close();
850 }
851
852 /*
853 * Try EDID preferred mode, if EDID or the suggested mode is not available,
854 * then try flat panel information.
855 * Fall back to VBE_DEFAULT_MODE.
856 */
857 int
vbe_default_mode(void)858 vbe_default_mode(void)
859 {
860 edid_res_list_t res;
861 struct resolution *rp;
862 int modenum;
863 uint_t width, height;
864
865 modenum = 0;
866 TAILQ_INIT(&res);
867 if (vbe_get_edid(&res)) {
868 while ((rp = TAILQ_FIRST(&res)) != NULL) {
869 if (modenum == 0) {
870 modenum = vbe_find_mode_xydm(
871 rp->width, rp->height, -1, -1);
872 }
873 TAILQ_REMOVE(&res, rp, next);
874 free(rp);
875 }
876 }
877
878 if (modenum == 0 &&
879 vbe_get_flatpanel(&width, &height)) {
880 modenum = vbe_find_mode_xydm(width, height, -1, -1);
881 }
882
883 /* Still no mode? Fall back to default. */
884 if (modenum == 0)
885 modenum = vbe_find_mode(VBE_DEFAULT_MODE);
886 return (modenum);
887 }
888
889 COMMAND_SET(framebuffer, "framebuffer", "framebuffer mode management",
890 command_vesa);
891
892 int
command_vesa(int argc,char * argv[])893 command_vesa(int argc, char *argv[])
894 {
895 char *arg, *cp;
896 int modenum = -1, n;
897
898 if (!vbe_check())
899 return (CMD_OK);
900
901 if (argc < 2)
902 goto usage;
903
904 if (strcmp(argv[1], "list") == 0) {
905 n = -1;
906 if (argc != 2 && argc != 3)
907 goto usage;
908
909 if (argc == 3) {
910 arg = argv[2];
911 errno = 0;
912 n = strtoul(arg, &cp, 0);
913 if (errno != 0 || *arg == '\0' || cp[0] != '\0') {
914 snprintf(command_errbuf,
915 sizeof (command_errbuf),
916 "depth should be an integer");
917 return (CMD_ERROR);
918 }
919 }
920 vbe_modelist(n);
921 return (CMD_OK);
922 }
923
924 if (strcmp(argv[1], "get") == 0) {
925 bool verbose = false;
926
927 if (argc > 2) {
928 if (argc > 3 || strcmp(argv[2], "-v") != 0)
929 goto usage;
930 verbose = true;
931 }
932
933 vbe_print_mode(verbose);
934 return (CMD_OK);
935 }
936
937 if (strcmp(argv[1], "off") == 0) {
938 if (argc != 2)
939 goto usage;
940
941 if (vbestate.vbe_mode == 0)
942 return (CMD_OK);
943
944 reset_font_flags();
945 bios_text_font(true);
946 bios_set_text_mode(VGA_TEXT_MODE);
947 plat_cons_update_mode(0);
948 return (CMD_OK);
949 }
950
951 if (strcmp(argv[1], "on") == 0) {
952 if (argc != 2)
953 goto usage;
954
955 modenum = vbe_default_mode();
956 if (modenum == 0) {
957 snprintf(command_errbuf, sizeof (command_errbuf),
958 "%s: no suitable VBE mode number found", argv[0]);
959 return (CMD_ERROR);
960 }
961 } else if (strcmp(argv[1], "set") == 0) {
962 if (argc != 3)
963 goto usage;
964
965 if (strncmp(argv[2], "0x", 2) == 0) {
966 arg = argv[2];
967 errno = 0;
968 n = strtoul(arg, &cp, 0);
969 if (errno != 0 || *arg == '\0' || cp[0] != '\0') {
970 snprintf(command_errbuf,
971 sizeof (command_errbuf),
972 "mode should be an integer");
973 return (CMD_ERROR);
974 }
975 modenum = vbe_find_mode_xydm(0, 0, 0, n);
976 } else if (strchr(argv[2], 'x') != NULL) {
977 modenum = vbe_find_mode(argv[2]);
978 }
979 } else {
980 goto usage;
981 }
982
983 if (modenum == 0) {
984 snprintf(command_errbuf, sizeof (command_errbuf),
985 "%s: mode %s not supported by firmware\n",
986 argv[0], argv[2]);
987 return (CMD_ERROR);
988 }
989
990 if (modenum >= VESA_MODE_BASE) {
991 if (vbestate.vbe_mode != modenum) {
992 reset_font_flags();
993 bios_text_font(false);
994 vbe_set_mode(modenum);
995 plat_cons_update_mode(1);
996 }
997 return (CMD_OK);
998 } else {
999 snprintf(command_errbuf, sizeof (command_errbuf),
1000 "%s: mode %s is not framebuffer mode\n", argv[0], argv[2]);
1001 return (CMD_ERROR);
1002 }
1003
1004 usage:
1005 snprintf(command_errbuf, sizeof (command_errbuf),
1006 "usage: %s on | off | get [-v] | list [depth] | "
1007 "set <display or VBE mode number>", argv[0]);
1008 return (CMD_ERROR);
1009 }
1010