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