1 /* $NetBSD: vbe.c,v 1.10 2019/12/15 03:38:17 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /* 30 * VESA BIOS Extensions routines 31 */ 32 33 #include <lib/libsa/stand.h> 34 #include <lib/libkern/libkern.h> 35 #include <machine/bootinfo.h> 36 #include "libi386.h" 37 #include "vbe.h" 38 39 extern const uint8_t rasops_cmap[]; 40 static uint8_t *vbe_edid = NULL; 41 static int vbe_edid_valid = 0; 42 43 static struct _vbestate { 44 int available; 45 int modenum; 46 } vbestate; 47 48 /* 49 * https://pdos.csail.mit.edu/6.828/2018/readings/hardware/vbe3.pdf 50 * p32 51 */ 52 #define VBE_MODEATTR_MODE_HARDWARE_SUPPORTED 0x0001u 53 #define VBE_MODEATTR_RESERVED_1 0x0002u 54 #define VBE_MODEATTR_TTY_OUTPUT_FUNCTIONS_SUPPORTED 0x0004u 55 #define VBE_MODEATTR_COLOR_MODE 0x0008u 56 #define VBE_MODEATTR_GRAPHICS_MODE 0x0010u 57 #define VBE_MODEATTR_VGA_COMPATIBLE_MODE 0x0020u 58 #define VBE_MODEATTR_VGA_COMPATIBLE_WINDOWD_MEMORY_MODE 0x0040u 59 #define VBE_MODEATTR_LINEAR_FRAME_BUFFER_MODE 0x0080u 60 #define VBE_MODEATTR_DOUBLE_SCAN_MODE 0x0100u 61 #define VBE_MODEATTR_INTERLACED_MODE 0x0200u 62 #define VBE_MODEATTR_HARDWARE_TRIPPLE_BUFFERING_SUPPORT 0x0400u 63 #define VBE_MODEATTR_HARDWARE_STEREOSCOPIC_SUPPORT 0x0800u 64 #define VBE_MODEATTR_DUAL_DISPLAY_START_ADDRESS_SUPPORT 0x1000u 65 #define VBE_MODEATTR_RESERVED_2 0x2000u 66 #define VBE_MODEATTR_RESERVED_3 0x4000u 67 #define VBE_MODEATTR_RESERVED_4 0x8000u 68 69 /* 70 * p36 71 */ 72 #define VBE_MEMMODEL_TEXT 0x00u 73 #define VBE_MEMMODEL_CGA 0x01u 74 #define VBE_MEMMODEL_HERCULES 0x02u 75 #define VBE_MEMMODEL_PLANAR 0x03u 76 #define VBE_MEMMODEL_PACKED_PIXEL 0x04u 77 #define VBE_MEMMODEL_NON_CHAIN_4_256 0x05u 78 #define VBE_MEMMODEL_DIRECT_COLOR 0x06u 79 #define VBE_MEMMODEL_YUV 0x07u 80 /* VESA Reserved 0x08u-0x0fu */ 81 /* OEM Reserved 0x10u-0xffU */ 82 83 84 static int 85 vbe_mode_is_supported(struct modeinfoblock *mi) 86 { 87 if ((mi->ModeAttributes & VBE_MODEATTR_MODE_HARDWARE_SUPPORTED) == 0) 88 return 0; /* mode not supported by hardware */ 89 if ((mi->ModeAttributes & VBE_MODEATTR_COLOR_MODE) == 0) 90 return 0; /* only color modes are supported */ 91 if ((mi->ModeAttributes & VBE_MODEATTR_GRAPHICS_MODE) == 0) 92 return 0; /* text mode */ 93 if ((mi->ModeAttributes & VBE_MODEATTR_LINEAR_FRAME_BUFFER_MODE) == 0) 94 return 0; /* linear fb not available */ 95 if (mi->NumberOfPlanes != 1) 96 return 0; /* planar mode not supported */ 97 if (mi->MemoryModel != VBE_MEMMODEL_PACKED_PIXEL /* Packed pixel */ && 98 mi->MemoryModel != VBE_MEMMODEL_DIRECT_COLOR /* Direct Color */) 99 return 0; /* unsupported pixel format */ 100 return 1; 101 } 102 103 static bool 104 vbe_check(void) 105 { 106 if (!vbestate.available) { 107 printf("VBE not available\n"); 108 return false; 109 } 110 return true; 111 } 112 113 void 114 vbe_init(void) 115 { 116 struct vbeinfoblock vbe; 117 118 memset(&vbe, 0, sizeof(vbe)); 119 memcpy(vbe.VbeSignature, "VBE2", 4); 120 if (biosvbe_info(&vbe) != 0x004f) 121 return; 122 if (memcmp(vbe.VbeSignature, "VESA", 4) != 0) 123 return; 124 125 vbestate.available = 1; 126 vbestate.modenum = 0; 127 } 128 129 int 130 vbe_available(void) 131 { 132 return vbestate.available; 133 } 134 135 int 136 vbe_set_palette(const uint8_t *cmap, int slot) 137 { 138 struct paletteentry pe; 139 int ret; 140 141 if (!vbe_check()) 142 return 1; 143 144 pe.Blue = cmap[2] >> 2; 145 pe.Green = cmap[1] >> 2; 146 pe.Red = cmap[0] >> 2; 147 pe.Alignment = 0; 148 149 ret = biosvbe_palette_data(0x0600, slot, &pe); 150 151 return ret == 0x004f ? 0 : 1; 152 } 153 154 int 155 vbe_set_mode(int modenum) 156 { 157 struct modeinfoblock mi; 158 struct btinfo_framebuffer fb; 159 int ret, i; 160 161 if (!vbe_check()) 162 return 1; 163 164 ret = biosvbe_get_mode_info(modenum, &mi); 165 if (ret != 0x004f) { 166 printf("mode 0x%x invalid\n", modenum); 167 return 1; 168 } 169 170 if (!vbe_mode_is_supported(&mi)) { 171 printf("mode 0x%x not supported\n", modenum); 172 return 1; 173 } 174 175 ret = biosvbe_set_mode(modenum); 176 if (ret != 0x004f) { 177 printf("mode 0x%x could not be set\n", modenum); 178 return 1; 179 } 180 181 /* Setup palette for packed pixel mode */ 182 if (mi.MemoryModel == 0x04) 183 for (i = 0; i < 256; i++) 184 vbe_set_palette(&rasops_cmap[i * 3], i); 185 186 memset(&fb, 0, sizeof(fb)); 187 fb.physaddr = (uint64_t)mi.PhysBasePtr & 0xffffffff; 188 fb.width = mi.XResolution; 189 fb.height = mi.YResolution; 190 fb.stride = mi.BytesPerScanLine; 191 fb.depth = mi.BitsPerPixel; 192 fb.flags = 0; 193 fb.rnum = mi.RedMaskSize; 194 fb.rpos = mi.RedFieldPosition; 195 fb.gnum = mi.GreenMaskSize; 196 fb.gpos = mi.GreenFieldPosition; 197 fb.bnum = mi.BlueMaskSize; 198 fb.bpos = mi.BlueFieldPosition; 199 fb.vbemode = modenum; 200 201 framebuffer_configure(&fb); 202 203 return 0; 204 } 205 206 int 207 vbe_commit(void) 208 { 209 int ret = 1; 210 211 if (vbestate.modenum > 0) { 212 ret = vbe_set_mode(vbestate.modenum); 213 if (ret) { 214 printf("WARNING: failed to set VBE mode 0x%x\n", 215 vbestate.modenum); 216 wait_sec(5); 217 } 218 } 219 return ret; 220 } 221 222 static void * 223 vbe_farptr(uint32_t farptr) 224 { 225 return VBEPHYPTR((((farptr & 0xffff0000) >> 12) + (farptr & 0xffff))); 226 } 227 228 static int 229 vbe_parse_mode_str(char *str, int *x, int *y, int *depth) 230 { 231 char *p; 232 233 p = str; 234 *x = strtoul(p, NULL, 0); 235 if (*x == 0) 236 return 0; 237 p = strchr(p, 'x'); 238 if (!p) 239 return 0; 240 ++p; 241 *y = strtoul(p, NULL, 0); 242 if (*y == 0) 243 return 0; 244 p = strchr(p, 'x'); 245 if (!p) 246 *depth = 8; 247 else { 248 ++p; 249 *depth = strtoul(p, NULL, 0); 250 if (*depth == 0) 251 return 0; 252 } 253 254 return 1; 255 } 256 257 static int 258 vbe_find_mode_xyd(int x, int y, int depth) 259 { 260 struct vbeinfoblock vbe; 261 struct modeinfoblock mi; 262 uint32_t farptr; 263 uint16_t mode; 264 int safety = 0; 265 266 memset(&vbe, 0, sizeof(vbe)); 267 memcpy(vbe.VbeSignature, "VBE2", 4); 268 if (biosvbe_info(&vbe) != 0x004f) 269 return 0; 270 if (memcmp(vbe.VbeSignature, "VESA", 4) != 0) 271 return 0; 272 farptr = vbe.VideoModePtr; 273 if (farptr == 0) 274 return 0; 275 276 while ((mode = *(uint16_t *)vbe_farptr(farptr)) != 0xffff) { 277 safety++; 278 farptr += 2; 279 if (safety == 100) 280 return 0; 281 if (biosvbe_get_mode_info(mode, &mi) != 0x004f) 282 continue; 283 /* we only care about linear modes here */ 284 if (vbe_mode_is_supported(&mi) == 0) 285 continue; 286 safety = 0; 287 if (mi.XResolution == x && 288 mi.YResolution == y && 289 mi.BitsPerPixel == depth) 290 return mode; 291 } 292 293 return 0; 294 } 295 296 static int 297 vbe_find_mode(char *str) 298 { 299 int x, y, depth; 300 301 if (!vbe_parse_mode_str(str, &x, &y, &depth)) 302 return 0; 303 304 return vbe_find_mode_xyd(x, y, depth); 305 } 306 307 static void 308 vbe_dump_mode(int modenum, struct modeinfoblock *mi) 309 { 310 printf("0x%x=%dx%dx%d", modenum, 311 mi->XResolution, mi->YResolution, mi->BitsPerPixel); 312 } 313 314 static int 315 vbe_get_edid(int *pwidth, int *pheight) 316 { 317 const uint8_t magic[] = EDID_MAGIC; 318 int ddc_caps, ret; 319 320 ddc_caps = biosvbe_ddc_caps(); 321 if (ddc_caps == 0) { 322 return 1; 323 } 324 325 if (vbe_edid == NULL) { 326 vbe_edid = alloc(128); 327 } 328 if (vbe_edid_valid == 0) { 329 ret = biosvbe_ddc_read_edid(0, vbe_edid); 330 if (ret != 0x004f) 331 return 1; 332 if (memcmp(vbe_edid, magic, sizeof(magic)) != 0) 333 return 1; 334 vbe_edid_valid = 1; 335 } 336 337 *pwidth = vbe_edid[EDID_DESC_BLOCK + 2] | 338 (((int)vbe_edid[EDID_DESC_BLOCK + 4] & 0xf0) << 4); 339 *pheight = vbe_edid[EDID_DESC_BLOCK + 5] | 340 (((int)vbe_edid[EDID_DESC_BLOCK + 7] & 0xf0) << 4); 341 342 return 0; 343 } 344 345 void 346 vbe_modelist(void) 347 { 348 struct vbeinfoblock vbe; 349 struct modeinfoblock mi; 350 uint32_t farptr; 351 uint16_t mode; 352 int nmodes = 0, safety = 0; 353 int ddc_caps, edid_width, edid_height; 354 355 if (!vbe_check()) 356 return; 357 358 ddc_caps = biosvbe_ddc_caps(); 359 if (ddc_caps & 3) { 360 printf("DDC"); 361 if (ddc_caps & 1) 362 printf(" [DDC1]"); 363 if (ddc_caps & 2) 364 printf(" [DDC2]"); 365 366 if (vbe_get_edid(&edid_width, &edid_height) != 0) 367 printf(": no EDID information\n"); 368 else 369 printf(": EDID %dx%d\n", edid_width, edid_height); 370 } 371 372 printf("Modes: "); 373 memset(&vbe, 0, sizeof(vbe)); 374 memcpy(vbe.VbeSignature, "VBE2", 4); 375 if (biosvbe_info(&vbe) != 0x004f) 376 goto done; 377 if (memcmp(vbe.VbeSignature, "VESA", 4) != 0) 378 goto done; 379 farptr = vbe.VideoModePtr; 380 if (farptr == 0) 381 goto done; 382 383 while ((mode = *(uint16_t *)vbe_farptr(farptr)) != 0xffff) { 384 safety++; 385 farptr += 2; 386 if (safety == 100) { 387 printf("[?] "); 388 break; 389 } 390 if (biosvbe_get_mode_info(mode, &mi) != 0x004f) 391 continue; 392 /* we only care about linear modes here */ 393 if (vbe_mode_is_supported(&mi) == 0) 394 continue; 395 safety = 0; 396 if (nmodes % 4 == 0) 397 printf("\n"); 398 else 399 printf(" "); 400 vbe_dump_mode(mode, &mi); 401 nmodes++; 402 } 403 404 done: 405 if (nmodes == 0) 406 printf("none found"); 407 printf("\n"); 408 } 409 410 void 411 command_vesa(char *cmd) 412 { 413 char arg[20]; 414 int modenum, edid_width, edid_height; 415 416 if (!vbe_check()) 417 return; 418 419 strlcpy(arg, cmd, sizeof(arg)); 420 421 if (strcmp(arg, "list") == 0) { 422 vbe_modelist(); 423 return; 424 } 425 426 if (strcmp(arg, "disabled") == 0 || strcmp(arg, "off") == 0) { 427 vbestate.modenum = 0; 428 return; 429 } 430 431 if (strcmp(arg, "enabled") == 0 || strcmp(arg, "on") == 0) { 432 if (vbe_get_edid(&edid_width, &edid_height) != 0) { 433 modenum = VBE_DEFAULT_MODE; 434 } else { 435 modenum = vbe_find_mode_xyd(edid_width, edid_height, 8); 436 if (modenum == 0) 437 modenum = VBE_DEFAULT_MODE; 438 } 439 } else if (strncmp(arg, "0x", 2) == 0) { 440 modenum = strtoul(arg, NULL, 0); 441 } else if (strchr(arg, 'x') != NULL) { 442 modenum = vbe_find_mode(arg); 443 if (modenum == 0) { 444 printf("mode %s not supported by firmware\n", arg); 445 return; 446 } 447 } else { 448 modenum = 0; 449 } 450 451 if (modenum >= 0x100) { 452 vbestate.modenum = modenum; 453 return; 454 } 455 456 printf("invalid flag, must be 'on', 'off', 'list', " 457 "a display mode, or a VBE mode number\n"); 458 } 459