1 /* 2 * Copyright 2004-2006, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "video.h" 8 #include "bios.h" 9 #include "vesa.h" 10 #include "vga.h" 11 #include "mmu.h" 12 #include "images.h" 13 14 #include <arch/cpu.h> 15 #include <boot/stage2.h> 16 #include <boot/platform.h> 17 #include <boot/menu.h> 18 #include <boot/kernel_args.h> 19 #include <util/list.h> 20 #include <drivers/driver_settings.h> 21 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 26 27 //#define TRACE_VIDEO 28 #ifdef TRACE_VIDEO 29 # define TRACE(x) dprintf x 30 #else 31 # define TRACE(x) ; 32 #endif 33 34 35 struct video_mode { 36 list_link link; 37 uint16 mode; 38 int32 width, height, bits_per_pixel; 39 }; 40 41 static vbe_info_block sInfo; 42 static video_mode *sMode, *sDefaultMode; 43 static bool sVesaCompatible; 44 struct list sModeList; 45 static addr_t sFrameBuffer; 46 static bool sModeChosen; 47 static bool sSettingsLoaded; 48 49 50 static int 51 compare_video_modes(video_mode *a, video_mode *b) 52 { 53 int compare = a->width - b->width; 54 if (compare != 0) 55 return compare; 56 57 compare = a->height - b->height; 58 if (compare != 0) 59 return compare; 60 61 // TODO: compare video_mode::mode? 62 return a->bits_per_pixel - b->bits_per_pixel; 63 } 64 65 66 /** Insert the video mode into the list, sorted by resolution and bit depth. 67 * Higher resolutions/depths come first. 68 */ 69 70 static void 71 add_video_mode(video_mode *videoMode) 72 { 73 video_mode *mode = NULL; 74 while ((mode = (video_mode *)list_get_next_item(&sModeList, mode)) != NULL) { 75 int compare = compare_video_modes(videoMode, mode); 76 if (compare == 0) { 77 // mode already exists 78 return; 79 } 80 81 if (compare > 0) 82 break; 83 } 84 85 list_insert_item_before(&sModeList, mode, videoMode); 86 } 87 88 89 static video_mode * 90 find_video_mode(int32 width, int32 height, int32 depth) 91 { 92 video_mode *mode = NULL; 93 while ((mode = (video_mode *)list_get_next_item(&sModeList, mode)) != NULL) { 94 if (mode->width == width 95 && mode->height == height 96 && mode->bits_per_pixel == depth) { 97 return mode; 98 } 99 } 100 101 return NULL; 102 } 103 104 105 static void 106 get_mode_from_settings(void) 107 { 108 if (sSettingsLoaded) 109 return; 110 111 void *handle = load_driver_settings("vesa"); 112 if (handle == NULL) 113 return; 114 115 const driver_settings *settings = get_driver_settings(handle); 116 if (settings == NULL) 117 goto out; 118 119 sSettingsLoaded = true; 120 121 for (int32 i = 0; i < settings->parameter_count; i++) { 122 driver_parameter ¶meter = settings->parameters[i]; 123 124 if (!strcmp(parameter.name, "mode") && parameter.value_count > 2) { 125 // parameter found, now get its values 126 int32 width = strtol(parameter.values[0], NULL, 0); 127 int32 height = strtol(parameter.values[1], NULL, 0); 128 int32 depth = strtol(parameter.values[2], NULL, 0); 129 130 // search mode that fits 131 132 video_mode *mode = find_video_mode(width, height, depth); 133 if (mode != NULL) 134 sMode = mode; 135 } 136 } 137 138 out: 139 unload_driver_settings(handle); 140 } 141 142 143 // #pragma mark - vga 144 145 146 static void 147 vga_set_palette(const uint8 *palette, int32 firstIndex, int32 numEntries) 148 { 149 out8(firstIndex, VGA_COLOR_WRITE_MODE); 150 // write VGA palette 151 for (int32 i = firstIndex; i < numEntries; i++) { 152 // VGA (usually) has only 6 bits per gun 153 out8(palette[i * 3 + 0] >> 2, VGA_COLOR_DATA); 154 out8(palette[i * 3 + 1] >> 2, VGA_COLOR_DATA); 155 out8(palette[i * 3 + 2] >> 2, VGA_COLOR_DATA); 156 } 157 } 158 159 160 static void 161 vga_enable_bright_background_colors(void) 162 { 163 // reset attribute controller 164 in8(VGA_INPUT_STATUS_1); 165 166 // select mode control register 167 out8(0x30, VGA_ATTRIBUTE_WRITE); 168 169 // read mode control register, change it (we need to clear bit 3), and write it back 170 uint8 mode = in8(VGA_ATTRIBUTE_READ) & 0xf7; 171 out8(mode, VGA_ATTRIBUTE_WRITE); 172 } 173 174 175 // #pragma mark - vesa 176 // VESA functions 177 178 179 static status_t 180 vesa_get_mode_info(uint16 mode, struct vbe_mode_info *modeInfo) 181 { 182 memset(modeInfo, 0, sizeof(vbe_mode_info)); 183 184 struct bios_regs regs; 185 regs.eax = 0x4f01; 186 regs.ecx = mode; 187 regs.es = ADDRESS_SEGMENT(modeInfo); 188 regs.edi = ADDRESS_OFFSET(modeInfo); 189 call_bios(0x10, ®s); 190 191 // %ah contains the error code 192 if ((regs.eax & 0xff00) != 0) 193 return B_ENTRY_NOT_FOUND; 194 195 return B_OK; 196 } 197 198 199 static status_t 200 vesa_get_vbe_info_block(vbe_info_block *info) 201 { 202 memset(info, 0, sizeof(vbe_info_block)); 203 info->signature = VBE2_SIGNATURE; 204 205 struct bios_regs regs; 206 regs.eax = 0x4f00; 207 regs.es = ADDRESS_SEGMENT(info); 208 regs.edi = ADDRESS_OFFSET(info); 209 call_bios(0x10, ®s); 210 211 // %ah contains the error code 212 if ((regs.eax & 0xff00) != 0) 213 return B_ERROR; 214 215 if (info->signature != VESA_SIGNATURE) 216 return B_ERROR; 217 218 dprintf("VESA version = %lx\n", info->version); 219 220 if (info->version.major < 2) { 221 dprintf("VESA support too old\n", info->version); 222 return B_ERROR; 223 } 224 225 info->oem_string = SEGMENTED_TO_LINEAR(info->oem_string); 226 info->mode_list = SEGMENTED_TO_LINEAR(info->mode_list); 227 dprintf("oem string: %s\n", (const char *)info->oem_string); 228 229 return B_OK; 230 } 231 232 233 static status_t 234 vesa_init(vbe_info_block *info, video_mode **_standardMode) 235 { 236 if (vesa_get_vbe_info_block(info) != B_OK) 237 return B_ERROR; 238 239 // fill mode list and find standard video mode 240 241 video_mode *standardMode = NULL; 242 243 for (int32 i = 0; true; i++) { 244 uint16 mode = ((uint16 *)info->mode_list)[i]; 245 if (mode == 0xffff) 246 break; 247 248 TRACE((" %lx: ", mode)); 249 250 struct vbe_mode_info modeInfo; 251 if (vesa_get_mode_info(mode, &modeInfo) == B_OK) { 252 TRACE(("%ld x %ld x %ld (a = %ld, mem = %ld, phy = %lx, p = %ld, b = %ld)\n", 253 modeInfo.width, modeInfo.height, modeInfo.bits_per_pixel, modeInfo.attributes, 254 modeInfo.memory_model, modeInfo.physical_base, modeInfo.num_planes, 255 modeInfo.num_banks)); 256 257 const uint32 requiredAttributes = MODE_ATTR_AVAILABLE | MODE_ATTR_GRAPHICS_MODE 258 | MODE_ATTR_COLOR_MODE | MODE_ATTR_LINEAR_BUFFER; 259 260 if (modeInfo.width >= 640 261 && modeInfo.physical_base != 0 262 && modeInfo.num_planes == 1 263 && (modeInfo.memory_model == MODE_MEMORY_PACKED_PIXEL 264 || modeInfo.memory_model == MODE_MEMORY_DIRECT_COLOR) 265 && (modeInfo.attributes & requiredAttributes) == requiredAttributes) { 266 // this mode fits our needs 267 video_mode *videoMode = (video_mode *)malloc(sizeof(struct video_mode)); 268 if (videoMode == NULL) 269 continue; 270 271 videoMode->mode = mode; 272 videoMode->width = modeInfo.width; 273 videoMode->height = modeInfo.height; 274 videoMode->bits_per_pixel = modeInfo.bits_per_pixel; 275 276 if (standardMode == NULL) 277 standardMode = videoMode; 278 else if (standardMode != NULL && modeInfo.bits_per_pixel <= 16) { 279 // for the standard mode, we prefer a bit depth of 16 280 // switch to the one with the higher resolution 281 // ToDo: is that always a good idea? for now we'll use 800x600 282 if (modeInfo.width >= standardMode->width && modeInfo.width <= 800) { 283 if (modeInfo.width != standardMode->width 284 || modeInfo.bits_per_pixel >= standardMode->bits_per_pixel) 285 standardMode = videoMode; 286 } 287 } 288 289 add_video_mode(videoMode); 290 } 291 } else 292 TRACE(("(failed)\n")); 293 } 294 295 if (standardMode == NULL) { 296 // no usable VESA mode found... 297 return B_ERROR; 298 } 299 300 *_standardMode = standardMode; 301 return B_OK; 302 } 303 304 305 #if 0 306 static status_t 307 vesa_get_mode(uint16 *_mode) 308 { 309 struct bios_regs regs; 310 regs.eax = 0x4f03; 311 call_bios(0x10, ®s); 312 313 if ((regs.eax & 0xffff) != 0x4f) 314 return B_ERROR; 315 316 *_mode = regs.ebx & 0xffff; 317 return B_OK; 318 } 319 #endif 320 321 322 static status_t 323 vesa_set_mode(uint16 mode) 324 { 325 struct bios_regs regs; 326 regs.eax = 0x4f02; 327 regs.ebx = (mode & SET_MODE_MASK) | SET_MODE_LINEAR_BUFFER; 328 call_bios(0x10, ®s); 329 330 if ((regs.eax & 0xffff) != 0x4f) 331 return B_ERROR; 332 333 #if 0 334 // make sure we have 8 bits per color channel 335 regs.eax = 0x4f08; 336 regs.ebx = 8 << 8; 337 call_bios(0x10, ®s); 338 #endif 339 340 return B_OK; 341 } 342 343 344 static status_t 345 vesa_set_palette(const uint8 *palette, int32 firstIndex, int32 numEntries) 346 { 347 // is this an 8 bit indexed color mode? 348 if (gKernelArgs.frame_buffer.depth != 8) 349 return B_BAD_TYPE; 350 351 #if 0 352 struct bios_regs regs; 353 regs.eax = 0x4f09; 354 regs.ebx = 0; 355 regs.ecx = numEntries; 356 regs.edx = firstIndex; 357 regs.es = (addr_t)palette >> 4; 358 regs.edi = (addr_t)palette & 0xf; 359 call_bios(0x10, ®s); 360 361 if ((regs.eax & 0xffff) != 0x4f) { 362 #endif 363 // the VESA call does not work, just try good old VGA mechanism 364 vga_set_palette(palette, firstIndex, numEntries); 365 #if 0 366 return B_ERROR; 367 } 368 #endif 369 return B_OK; 370 } 371 372 373 // #pragma mark - 374 375 376 bool 377 video_mode_hook(Menu *menu, MenuItem *item) 378 { 379 // find selected mode 380 video_mode *mode = NULL; 381 382 menu = item->Submenu(); 383 item = menu->FindMarked(); 384 if (item != NULL) { 385 switch (menu->IndexOf(item)) { 386 case 0: 387 // "Default" mode special 388 sMode = sDefaultMode; 389 sModeChosen = false; 390 return true; 391 case 1: 392 // "Standard VGA" mode special 393 // sets sMode to NULL which triggers VGA mode 394 break; 395 default: 396 mode = (video_mode *)item->Data(); 397 break; 398 } 399 } 400 401 if (mode != sMode) { 402 // update standard mode 403 // ToDo: update fb settings! 404 sMode = mode; 405 } 406 407 sModeChosen = true; 408 return true; 409 } 410 411 412 Menu * 413 video_mode_menu() 414 { 415 Menu *menu = new(nothrow) Menu(CHOICE_MENU, "Select Video Mode"); 416 MenuItem *item; 417 418 menu->AddItem(item = new(nothrow) MenuItem("Default")); 419 item->SetMarked(true); 420 item->Select(true); 421 item->SetHelpText("The Default video mode is the one currently configured in " 422 "the system. If there is no mode configured yet, a viable mode will be chosen " 423 "automatically."); 424 425 menu->AddItem(new(nothrow) MenuItem("Standard VGA")); 426 427 video_mode *mode = NULL; 428 while ((mode = (video_mode *)list_get_next_item(&sModeList, mode)) != NULL) { 429 char label[64]; 430 sprintf(label, "%ldx%ld %ld bit", mode->width, mode->height, mode->bits_per_pixel); 431 432 menu->AddItem(item = new(nothrow) MenuItem(label)); 433 item->SetData(mode); 434 } 435 436 menu->AddSeparatorItem(); 437 menu->AddItem(item = new(nothrow) MenuItem("Return to main menu")); 438 item->SetType(MENU_ITEM_NO_CHOICE); 439 440 return menu; 441 } 442 443 444 static void 445 set_vga_mode(void) 446 { 447 // sets 640x480 16 colors graphics mode 448 bios_regs regs; 449 regs.eax = 0x12; 450 call_bios(0x10, ®s); 451 } 452 453 454 static void 455 set_text_mode(void) 456 { 457 // sets 80x25 text console 458 bios_regs regs; 459 regs.eax = 3; 460 call_bios(0x10, ®s); 461 } 462 463 464 // #pragma mark - blit 465 466 467 static void 468 blit32(const uint8 *data, uint16 width, uint16 height, 469 const uint8 *palette, uint16 left, uint16 top) 470 { 471 uint32 *start = (uint32 *)sFrameBuffer + gKernelArgs.frame_buffer.width * top + left; 472 473 for (int32 y = 0; y < height; y++) { 474 for (int32 x = 0; x < width; x++) { 475 uint16 color = data[y * width + x] * 3; 476 477 start[x] = (palette[color + 0] << 16) | (palette[color + 1] << 8) | (palette[color + 2]); 478 } 479 480 start += gKernelArgs.frame_buffer.width; 481 } 482 } 483 484 485 static void 486 blit24(const uint8 *data, uint16 width, uint16 height, 487 const uint8 *palette, uint16 left, uint16 top) 488 { 489 uint8 *start = (uint8 *)sFrameBuffer + gKernelArgs.frame_buffer.width * 3 * top + 3 * left; 490 491 for (int32 y = 0; y < height; y++) { 492 for (int32 x = 0; x < width; x++) { 493 uint16 color = data[y * width + x] * 3; 494 uint32 index = x * 3; 495 496 start[index + 0] = palette[color + 2]; 497 start[index + 1] = palette[color + 1]; 498 start[index + 2] = palette[color + 0]; 499 } 500 501 start += gKernelArgs.frame_buffer.width * 3; 502 } 503 } 504 505 506 static void 507 blit16(const uint8 *data, uint16 width, uint16 height, 508 const uint8 *palette, uint16 left, uint16 top) 509 { 510 uint16 *start = (uint16 *)sFrameBuffer + gKernelArgs.frame_buffer.width * top + left; 511 512 for (int32 y = 0; y < height; y++) { 513 for (int32 x = 0; x < width; x++) { 514 uint16 color = data[y * width + x] * 3; 515 516 start[x] = ((palette[color + 0] >> 3) << 11) | ((palette[color + 1] >> 2) << 5) 517 | ((palette[color + 2] >> 3)); 518 } 519 520 start += gKernelArgs.frame_buffer.width; 521 } 522 } 523 524 525 static void 526 blit15(const uint8 *data, uint16 width, uint16 height, 527 const uint8 *palette, uint16 left, uint16 top) 528 { 529 uint16 *start = (uint16 *)sFrameBuffer + gKernelArgs.frame_buffer.width * top + left; 530 531 for (int32 y = 0; y < height; y++) { 532 for (int32 x = 0; x < width; x++) { 533 uint16 color = data[y * width + x] * 3; 534 535 start[x] = ((palette[color + 0] >> 3) << 10) | ((palette[color + 1] >> 3) << 5) 536 | ((palette[color + 2] >> 3)); 537 } 538 539 start += gKernelArgs.frame_buffer.width; 540 } 541 } 542 543 544 static void 545 blit8(const uint8 *data, uint16 width, uint16 height, 546 const uint8 *palette, uint16 left, uint16 top) 547 { 548 if (vesa_set_palette((const uint8 *)kPalette, 0, 256) != B_OK) 549 dprintf("set palette failed!\n"); 550 551 addr_t start = sFrameBuffer + gKernelArgs.frame_buffer.width * top + left; 552 553 for (int32 i = 0; i < height; i++) { 554 memcpy((void *)(start + gKernelArgs.frame_buffer.width * i), 555 &data[i * width], width); 556 } 557 } 558 559 560 static void 561 blit4(const uint8 *data, uint16 width, uint16 height, 562 const uint8 *palette, uint16 left, uint16 top) 563 { 564 // vga_set_palette((const uint8 *)kPalette16, 0, 16); 565 // ToDo: no boot logo yet in VGA mode 566 #if 1 567 // this draws 16 big rectangles in all the available colors 568 uint8 *bits = (uint8 *)sFrameBuffer; 569 uint32 bytesPerRow = 80; 570 for (int32 i = 0; i < 32; i++) { 571 bits[9 * bytesPerRow + i + 2] = 0x55; 572 bits[30 * bytesPerRow + i + 2] = 0xaa; 573 } 574 575 for (int32 y = 10; y < 30; y++) { 576 for (int32 i = 0; i < 16; i++) { 577 out16((15 << 8) | 0x02, VGA_SEQUENCER_INDEX); 578 bits[32 * bytesPerRow + i*2 + 2] = i; 579 580 if (i & 1) { 581 out16((1 << 8) | 0x02, VGA_SEQUENCER_INDEX); 582 bits[y * bytesPerRow + i*2 + 2] = 0xff; 583 bits[y * bytesPerRow + i*2 + 3] = 0xff; 584 } 585 if (i & 2) { 586 out16((2 << 8) | 0x02, VGA_SEQUENCER_INDEX); 587 bits[y * bytesPerRow + i*2 + 2] = 0xff; 588 bits[y * bytesPerRow + i*2 + 3] = 0xff; 589 } 590 if (i & 4) { 591 out16((4 << 8) | 0x02, VGA_SEQUENCER_INDEX); 592 bits[y * bytesPerRow + i*2 + 2] = 0xff; 593 bits[y * bytesPerRow + i*2 + 3] = 0xff; 594 } 595 if (i & 8) { 596 out16((8 << 8) | 0x02, VGA_SEQUENCER_INDEX); 597 bits[y * bytesPerRow + i*2 + 2] = 0xff; 598 bits[y * bytesPerRow + i*2 + 3] = 0xff; 599 } 600 } 601 } 602 603 // enable all planes again 604 out16((15 << 8) | 0x02, VGA_SEQUENCER_INDEX); 605 #endif 606 } 607 608 609 static void 610 blit_8bit_image(const uint8 *data, uint16 width, uint16 height, 611 const uint8 *palette, uint16 left, uint16 top) 612 { 613 switch (gKernelArgs.frame_buffer.depth) { 614 case 4: 615 return blit4(data, width, height, palette, left, top); 616 case 8: 617 return blit8(data, width, height, palette, left, top); 618 case 15: 619 return blit15(data, width, height, palette, left, top); 620 case 16: 621 return blit16(data, width, height, palette, left, top); 622 case 24: 623 return blit24(data, width, height, palette, left, top); 624 case 32: 625 return blit32(data, width, height, palette, left, top); 626 } 627 } 628 629 630 // #pragma mark - 631 632 633 extern "C" void 634 platform_switch_to_logo(void) 635 { 636 // in debug mode, we'll never show the logo 637 if ((platform_boot_options() & BOOT_OPTION_DEBUG_OUTPUT) != 0) 638 return; 639 640 addr_t lastBase = gKernelArgs.frame_buffer.physical_buffer.start; 641 size_t lastSize = gKernelArgs.frame_buffer.physical_buffer.size; 642 int32 bytesPerPixel = 1; 643 644 if (sVesaCompatible && sMode != NULL) { 645 if (!sModeChosen) 646 get_mode_from_settings(); 647 648 if (vesa_set_mode(sMode->mode) != B_OK) 649 goto fallback; 650 651 struct vbe_mode_info modeInfo; 652 if (vesa_get_mode_info(sMode->mode, &modeInfo) != B_OK) 653 goto fallback; 654 655 bytesPerPixel = (modeInfo.bits_per_pixel + 7) / 8; 656 657 gKernelArgs.frame_buffer.width = modeInfo.width; 658 gKernelArgs.frame_buffer.height = modeInfo.height; 659 gKernelArgs.frame_buffer.depth = modeInfo.bits_per_pixel; 660 gKernelArgs.frame_buffer.physical_buffer.size = gKernelArgs.frame_buffer.width 661 * gKernelArgs.frame_buffer.height * bytesPerPixel; 662 gKernelArgs.frame_buffer.physical_buffer.start = modeInfo.physical_base; 663 } else { 664 fallback: 665 // use standard VGA mode 640x480x4 666 set_vga_mode(); 667 668 gKernelArgs.frame_buffer.width = 640; 669 gKernelArgs.frame_buffer.height = 480; 670 gKernelArgs.frame_buffer.depth = 4; 671 gKernelArgs.frame_buffer.physical_buffer.size = gKernelArgs.frame_buffer.width 672 * gKernelArgs.frame_buffer.height / 2; 673 gKernelArgs.frame_buffer.physical_buffer.start = 0xa0000; 674 } 675 676 gKernelArgs.frame_buffer.enabled = 1; 677 678 // If the new frame buffer is either larger than the old one or located at 679 // a different address, we need to remap it, so we first have to throw 680 // away its previous mapping 681 if (lastBase != 0 682 && (lastBase != gKernelArgs.frame_buffer.physical_buffer.start 683 || lastSize < gKernelArgs.frame_buffer.physical_buffer.size)) { 684 mmu_free((void *)sFrameBuffer, lastSize); 685 lastBase = 0; 686 } 687 if (lastBase == 0) { 688 // the graphics memory has not been mapped yet! 689 sFrameBuffer = mmu_map_physical_memory(gKernelArgs.frame_buffer.physical_buffer.start, 690 gKernelArgs.frame_buffer.physical_buffer.size, kDefaultPageFlags); 691 } 692 693 // clear the video memory 694 // ToDo: this shouldn't be necessary on real hardware (and Bochs), but 695 // at least booting with Qemu looks ugly when this is missing 696 memset((void *)sFrameBuffer, 0, gKernelArgs.frame_buffer.physical_buffer.size); 697 698 // ToDo: the boot image is only a temporary solution - it should be 699 // provided by the loader itself, as well as the blitting routines. 700 // The image should be compressed, too. 701 702 blit_8bit_image(kImageData, kWidth, kHeight, kPalette, 703 gKernelArgs.frame_buffer.width - kWidth - 40, 704 gKernelArgs.frame_buffer.height - kHeight - 60); 705 } 706 707 708 extern "C" void 709 platform_switch_to_text_mode(void) 710 { 711 if (!gKernelArgs.frame_buffer.enabled) { 712 vga_enable_bright_background_colors(); 713 return; 714 } 715 716 set_text_mode(); 717 gKernelArgs.frame_buffer.enabled = 0; 718 719 vga_enable_bright_background_colors(); 720 } 721 722 723 extern "C" status_t 724 platform_init_video(void) 725 { 726 gKernelArgs.frame_buffer.enabled = 0; 727 list_init(&sModeList); 728 729 set_text_mode(); 730 // You may wonder why we do this here: 731 // Obviously, some graphics card BIOS implementations don't 732 // report all available modes unless you've done this before 733 // getting the VESA information. 734 // One example of those is the SiS 630 chipset in my laptop. 735 736 sVesaCompatible = vesa_init(&sInfo, &sDefaultMode) == B_OK; 737 if (!sVesaCompatible) { 738 TRACE(("No VESA compatible graphics!\n")); 739 return B_ERROR; 740 } 741 742 sMode = sDefaultMode; 743 744 TRACE(("VESA compatible graphics!\n")); 745 746 return B_OK; 747 } 748 749