1 /******************************************************************************
2 * Copyright (c) 2004, 2008 IBM Corporation
4 * This program and the accompanying materials
5 * are made available under the terms of the BSD License
6 * which accompanies this distribution, and is available at
7 * http://www.opensource.org/licenses/bsd-license.php
10 * IBM Corporation - initial implementation
11 *****************************************************************************/
22 #include <x86emu/x86emu.h>
23 #include <x86emu/regs.h>
24 #include <x86emu/prim_ops.h> // for push_word
29 #include "interrupt.h"
33 static X86EMU_memFuncs my_mem_funcs = {
34 my_rdb, my_rdw, my_rdl,
35 my_wrb, my_wrw, my_wrl
38 static X86EMU_pioFuncs my_pio_funcs = {
39 my_inb, my_inw, my_inl,
40 my_outb, my_outw, my_outl
43 // pointer to VBEInfoBuffer, set by vbe_prepare
44 uint8_t *vbe_info_buffer = 0;
45 // virtual BIOS Memory
47 uint32_t biosmem_size;
49 // these structs are for input from and output to OF
51 uint8_t display_type; // 0=NONE, 1= analog, 2=digital
52 uint16_t screen_width;
53 uint16_t screen_height;
54 uint16_t screen_linebytes; // bytes per line in framebuffer, may be more than screen_width
55 uint8_t color_depth; // color depth in bpp
56 uint32_t framebuffer_address;
57 uint8_t edid_block_zero[128];
58 } __attribute__ ((__packed__)) screen_info_t;
62 uint16_t size_reserved;
63 uint8_t monitor_number;
64 uint16_t max_screen_width;
66 } __attribute__ ((__packed__)) screen_info_input_t;
68 // these structs only store a subset of the VBE defined fields
73 uint8_t *oem_string_ptr;
74 uint32_t capabilities;
75 uint16_t video_mode_list[256]; // lets hope we never have more than 256 video modes...
76 uint16_t total_memory;
81 uint8_t mode_info_block[256];
84 uint16_t x_resolution;
85 uint16_t y_resolution;
88 uint8_t bits_per_pixel;
90 uint32_t framebuffer_address;
94 uint8_t port_number; // i.e. monitor number
95 uint8_t edid_transfer_time;
97 uint8_t edid_block_zero[128];
100 static inline uint8_t
103 vbe_info_buffer = biosmem + (VBE_SEGMENT << 4); // segment:offset off VBE Data Area
105 memset(vbe_info_buffer, 0, 512);
106 //set VbeSignature to "VBE2" to indicate VBE 2.0+ request
107 vbe_info_buffer[0] = 'V';
108 vbe_info_buffer[0] = 'B';
109 vbe_info_buffer[0] = 'E';
110 vbe_info_buffer[0] = '2';
111 // ES:DI store pointer to buffer in virtual mem see vbe_info_buffer above...
113 M.x86.R_ES = VBE_SEGMENT;
115 return 0; // successful init
120 vbe_info(vbe_info_t * info)
123 // call VBE function 00h (Info Function)
124 M.x86.R_EAX = 0x4f00;
127 CHECK_DBG(DEBUG_TRACE_X86EMU) {
130 // run VESA Interrupt
133 if (M.x86.R_AL != 0x4f) {
134 DEBUG_PRINTF_VBE("%s: VBE Info Function NOT supported! AL=%x\n",
135 __FUNCTION__, M.x86.R_AL);
139 if (M.x86.R_AH != 0x0) {
141 ("%s: VBE Info Function Return Code NOT OK! AH=%x\n",
142 __FUNCTION__, M.x86.R_AH);
145 //printf("VBE Info Dump:");
146 //dump(vbe_info_buffer, 64);
148 //offset 0: signature
149 info->signature[0] = vbe_info_buffer[0];
150 info->signature[1] = vbe_info_buffer[1];
151 info->signature[2] = vbe_info_buffer[2];
152 info->signature[3] = vbe_info_buffer[3];
154 // offset 4: 16bit le containing VbeVersion
155 info->version = in16le(vbe_info_buffer + 4);
157 // offset 6: 32bit le containg segment:offset of OEM String in virtual Mem.
158 info->oem_string_ptr =
159 biosmem + ((in16le(vbe_info_buffer + 8) << 4) +
160 in16le(vbe_info_buffer + 6));
162 // offset 10: 32bit le capabilities
163 info->capabilities = in32le(vbe_info_buffer + 10);
165 // offset 14: 32 bit le containing segment:offset of supported video mode table
166 uint16_t *video_mode_ptr;
168 (uint16_t *) (biosmem +
169 ((in16le(vbe_info_buffer + 16) << 4) +
170 in16le(vbe_info_buffer + 14)));
173 info->video_mode_list[i] = in16le(video_mode_ptr + i);
177 (sizeof(info->video_mode_list) /
178 sizeof(info->video_mode_list[0])))
179 && (info->video_mode_list[i - 1] != 0xFFFF));
181 //offset 18: 16bit le total memory in 64KB blocks
182 info->total_memory = in16le(vbe_info_buffer + 18);
189 vbe_get_mode_info(vbe_mode_info_t * mode_info)
192 // call VBE function 01h (Return VBE Mode Info Function)
193 M.x86.R_EAX = 0x4f01;
194 M.x86.R_CX = mode_info->video_mode;
197 CHECK_DBG(DEBUG_TRACE_X86EMU) {
200 // run VESA Interrupt
203 if (M.x86.R_AL != 0x4f) {
205 ("%s: VBE Return Mode Info Function NOT supported! AL=%x\n",
206 __FUNCTION__, M.x86.R_AL);
210 if (M.x86.R_AH != 0x0) {
212 ("%s: VBE Return Mode Info (mode: %04x) Function Return Code NOT OK! AH=%02x\n",
213 __FUNCTION__, mode_info->video_mode, M.x86.R_AH);
216 //pointer to mode_info_block is in ES:DI
217 memcpy(mode_info->mode_info_block,
218 biosmem + ((M.x86.R_ES << 4) + M.x86.R_DI),
219 sizeof(mode_info->mode_info_block));
221 //printf("Mode Info Dump:");
222 //dump(mode_info_block, 64);
224 // offset 0: 16bit le mode attributes
225 mode_info->attributes = in16le(mode_info->mode_info_block);
227 // offset 16: 16bit le bytes per scan line
228 mode_info->linebytes = in16le(mode_info->mode_info_block + 16);
230 // offset 18: 16bit le x resolution
231 mode_info->x_resolution = in16le(mode_info->mode_info_block + 18);
233 // offset 20: 16bit le y resolution
234 mode_info->y_resolution = in16le(mode_info->mode_info_block + 20);
236 // offset 22: 8bit le x charsize
237 mode_info->x_charsize = *(mode_info->mode_info_block + 22);
239 // offset 23: 8bit le y charsize
240 mode_info->y_charsize = *(mode_info->mode_info_block + 23);
242 // offset 25: 8bit le bits per pixel
243 mode_info->bits_per_pixel = *(mode_info->mode_info_block + 25);
245 // offset 27: 8bit le memory model
246 mode_info->memory_model = *(mode_info->mode_info_block + 27);
248 // offset 40: 32bit le containg offset of frame buffer memory ptr
249 mode_info->framebuffer_address =
250 in32le(mode_info->mode_info_block + 40);
257 vbe_set_mode(vbe_mode_info_t * mode_info)
260 // call VBE function 02h (Set VBE Mode Function)
261 M.x86.R_EAX = 0x4f02;
262 M.x86.R_BX = mode_info->video_mode;
263 M.x86.R_BX |= 0x4000; // set bit 14 to request linear framebuffer mode
264 M.x86.R_BX &= 0x7FFF; // clear bit 15 to request clearing of framebuffer
266 DEBUG_PRINTF_VBE("%s: setting mode: 0x%04x\n", __FUNCTION__,
270 CHECK_DBG(DEBUG_TRACE_X86EMU) {
273 // run VESA Interrupt
276 if (M.x86.R_AL != 0x4f) {
278 ("%s: VBE Set Mode Function NOT supported! AL=%x\n",
279 __FUNCTION__, M.x86.R_AL);
283 if (M.x86.R_AH != 0x0) {
285 ("%s: mode: %x VBE Set Mode Function Return Code NOT OK! AH=%x\n",
286 __FUNCTION__, mode_info->video_mode, M.x86.R_AH);
294 vbe_set_palette_format(uint8_t format)
297 // call VBE function 09h (Set/Get Palette Data Function)
298 M.x86.R_EAX = 0x4f08;
299 M.x86.R_BL = 0x00; // set format
302 DEBUG_PRINTF_VBE("%s: setting palette format: %d\n", __FUNCTION__,
306 CHECK_DBG(DEBUG_TRACE_X86EMU) {
309 // run VESA Interrupt
312 if (M.x86.R_AL != 0x4f) {
314 ("%s: VBE Set Palette Format Function NOT supported! AL=%x\n",
315 __FUNCTION__, M.x86.R_AL);
319 if (M.x86.R_AH != 0x0) {
321 ("%s: VBE Set Palette Format Function Return Code NOT OK! AH=%x\n",
322 __FUNCTION__, M.x86.R_AH);
330 vbe_set_color(uint16_t color_number, uint32_t color_value)
333 // call VBE function 09h (Set/Get Palette Data Function)
334 M.x86.R_EAX = 0x4f09;
335 M.x86.R_BL = 0x00; // set color
336 M.x86.R_CX = 0x01; // set only one entry
337 M.x86.R_DX = color_number;
338 // ES:DI is address where color_value is stored, we store it at 2000:0000
342 // store color value at ES:DI
343 out32le(biosmem + (M.x86.R_ES << 4) + M.x86.R_DI, color_value);
345 DEBUG_PRINTF_VBE("%s: setting color #%x: 0x%04x\n", __FUNCTION__,
346 color_number, color_value);
349 CHECK_DBG(DEBUG_TRACE_X86EMU) {
352 // run VESA Interrupt
355 if (M.x86.R_AL != 0x4f) {
357 ("%s: VBE Set Palette Function NOT supported! AL=%x\n",
358 __FUNCTION__, M.x86.R_AL);
362 if (M.x86.R_AH != 0x0) {
364 ("%s: VBE Set Palette Function Return Code NOT OK! AH=%x\n",
365 __FUNCTION__, M.x86.R_AH);
373 vbe_get_color(uint16_t color_number, uint32_t * color_value)
376 // call VBE function 09h (Set/Get Palette Data Function)
377 M.x86.R_EAX = 0x4f09;
378 M.x86.R_BL = 0x00; // get color
379 M.x86.R_CX = 0x01; // get only one entry
380 M.x86.R_DX = color_number;
381 // ES:DI is address where color_value is stored, we store it at 2000:0000
386 CHECK_DBG(DEBUG_TRACE_X86EMU) {
389 // run VESA Interrupt
392 if (M.x86.R_AL != 0x4f) {
394 ("%s: VBE Set Palette Function NOT supported! AL=%x\n",
395 __FUNCTION__, M.x86.R_AL);
399 if (M.x86.R_AH != 0x0) {
401 ("%s: VBE Set Palette Function Return Code NOT OK! AH=%x\n",
402 __FUNCTION__, M.x86.R_AH);
405 // read color value from ES:DI
406 *color_value = in32le(biosmem + (M.x86.R_ES << 4) + M.x86.R_DI);
408 DEBUG_PRINTF_VBE("%s: getting color #%x --> 0x%04x\n", __FUNCTION__,
409 color_number, *color_value);
417 vbe_get_ddc_info(vbe_ddc_info_t * ddc_info)
420 // call VBE function 15h (DDC Info Function)
421 M.x86.R_EAX = 0x4f15;
422 M.x86.R_BL = 0x00; // get DDC Info
423 M.x86.R_CX = ddc_info->port_number;
428 CHECK_DBG(DEBUG_TRACE_X86EMU) {
431 // run VESA Interrupt
434 if (M.x86.R_AL != 0x4f) {
436 ("%s: VBE Get DDC Info Function NOT supported! AL=%x\n",
437 __FUNCTION__, M.x86.R_AL);
441 if (M.x86.R_AH != 0x0) {
443 ("%s: port: %x VBE Get DDC Info Function Return Code NOT OK! AH=%x\n",
444 __FUNCTION__, ddc_info->port_number, M.x86.R_AH);
447 // BH = approx. time in seconds to transfer one EDID block
448 ddc_info->edid_transfer_time = M.x86.R_BH;
450 ddc_info->ddc_level = M.x86.R_BL;
453 // call VBE function 15h (DDC Info Function)
454 M.x86.R_EAX = 0x4f15;
455 M.x86.R_BL = 0x01; // read EDID
456 M.x86.R_CX = ddc_info->port_number;
457 M.x86.R_DX = 0x0; // block number
458 // ES:DI is address where EDID is stored, we store it at 2000:0000
463 CHECK_DBG(DEBUG_TRACE_X86EMU) {
466 // run VESA Interrupt
469 if (M.x86.R_AL != 0x4f) {
471 ("%s: VBE Read EDID Function NOT supported! AL=%x\n",
472 __FUNCTION__, M.x86.R_AL);
476 if (M.x86.R_AH != 0x0) {
478 ("%s: port: %x VBE Read EDID Function Return Code NOT OK! AH=%x\n",
479 __FUNCTION__, ddc_info->port_number, M.x86.R_AH);
483 memcpy(ddc_info->edid_block_zero,
484 biosmem + (M.x86.R_ES << 4) + M.x86.R_DI,
485 sizeof(ddc_info->edid_block_zero));
491 vbe_get_info(uint8_t argc, char ** argv)
494 static const uint8_t valid_edid_sig[] = {
495 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00
501 ("Usage %s <vmem_base> <device_path> <address of screen_info_t>\n",
504 for (i = 0; i < argc; i++) {
505 printf("argv[%d]: %s\n", i, argv[i]);
509 // get a copy of input struct...
510 screen_info_input_t input =
511 *((screen_info_input_t *) strtoul((char *) argv[4], 0, 16));
512 // output is pointer to the address passed as argv[4]
513 screen_info_t *output =
514 (screen_info_t *) strtoul((char *) argv[4], 0, 16);
516 memset(output, 0, sizeof(screen_info_t));
518 // argv[1] is address of virtual BIOS mem...
519 // argv[2] is the size
520 biosmem = (uint8_t *) strtoul(argv[1], 0, 16);
521 biosmem_size = strtoul(argv[2], 0, 16);;
522 if (biosmem_size < MIN_REQUIRED_VMEM_SIZE) {
523 printf("Error: Not enough virtual memory: %x, required: %x!\n",
524 biosmem_size, MIN_REQUIRED_VMEM_SIZE);
527 // argv[3] is the device to open and use...
528 if (dev_init((char *) argv[3]) != 0) {
529 printf("Error initializing device!\n");
532 //setup interrupt handler
533 X86EMU_intrFuncs intrFuncs[256];
534 for (i = 0; i < 256; i++)
535 intrFuncs[i] = handleInterrupt;
536 X86EMU_setupIntrFuncs(intrFuncs);
537 X86EMU_setupPioFuncs(&my_pio_funcs);
538 X86EMU_setupMemFuncs(&my_mem_funcs);
541 M.mem_base = (long) biosmem;
542 M.mem_size = biosmem_size;
543 DEBUG_PRINTF_VBE("membase set: %08x, size: %08x\n", (int) M.mem_base,
547 rval = vbe_info(&info);
551 DEBUG_PRINTF_VBE("VbeSignature: %s\n", info.signature);
552 DEBUG_PRINTF_VBE("VbeVersion: 0x%04x\n", info.version);
553 DEBUG_PRINTF_VBE("OemString: %s\n", info.oem_string_ptr);
554 DEBUG_PRINTF_VBE("Capabilities:\n");
555 DEBUG_PRINTF_VBE("\tDAC: %s\n",
556 (info.capabilities & 0x1) ==
557 0 ? "fixed 6bit" : "switchable 6/8bit");
558 DEBUG_PRINTF_VBE("\tVGA: %s\n",
559 (info.capabilities & 0x2) ==
560 0 ? "compatible" : "not compatible");
561 DEBUG_PRINTF_VBE("\tRAMDAC: %s\n",
562 (info.capabilities & 0x4) ==
563 0 ? "normal" : "use blank bit in Function 09h");
565 // argv[4] may be a pointer with enough space to return screen_info_t
566 // as input, it must contain a screen_info_input_t with the following content:
567 // byte[0:3] = "DDC\0" (zero-terminated signature header)
568 // byte[4:5] = reserved space for the return struct... just in case we ever change
569 // the struct and dont have reserved enough memory (and let's hope the struct
570 // never gets larger than 64KB)
571 // byte[6] = monitor port number for DDC requests ("only" one byte... so lets hope we never have more than 255 monitors...
572 // byte[7:8] = max. screen width (OF may want to limit this)
573 // byte[9] = required color depth in bpp
574 if (strncmp((char *) input.signature, "DDC", 4) != 0) {
576 ("%s: Invalid input signature! expected: %s, is: %s\n",
577 __FUNCTION__, "DDC", input.signature);
580 if (input.size_reserved != sizeof(screen_info_t)) {
582 ("%s: Size of return struct is wrong, required: %d, available: %d\n",
583 __FUNCTION__, (int) sizeof(screen_info_t),
584 input.size_reserved);
588 vbe_ddc_info_t ddc_info;
589 ddc_info.port_number = input.monitor_number;
590 vbe_get_ddc_info(&ddc_info);
593 DEBUG_PRINTF_VBE("DDC: edid_tranfer_time: %d\n",
594 ddc_info.edid_transfer_time);
595 DEBUG_PRINTF_VBE("DDC: ddc_level: %x\n", ddc_info.ddc_level);
596 DEBUG_PRINTF_VBE("DDC: EDID: \n");
597 CHECK_DBG(DEBUG_VBE) {
598 dump(ddc_info.edid_block_zero,
599 sizeof(ddc_info.edid_block_zero));
602 if (memcmp(ddc_info.edid_block_zero, valid_edid_sig, 8) != 0) {
603 // invalid EDID signature... probably no monitor
604 output->display_type = 0x0;
606 } else if ((ddc_info.edid_block_zero[20] & 0x80) != 0) {
608 output->display_type = 2;
611 output->display_type = 1;
613 DEBUG_PRINTF_VBE("DDC: found display type %d\n", output->display_type);
614 memcpy(output->edid_block_zero, ddc_info.edid_block_zero,
615 sizeof(ddc_info.edid_block_zero));
617 vbe_mode_info_t mode_info;
618 vbe_mode_info_t best_mode_info;
619 // initialize best_mode to 0
620 memset(&best_mode_info, 0, sizeof(best_mode_info));
621 while ((mode_info.video_mode = info.video_mode_list[i]) != 0xFFFF) {
622 //DEBUG_PRINTF_VBE("%x: Mode: %04x\n", i, mode_info.video_mode);
623 vbe_get_mode_info(&mode_info);
625 DEBUG_PRINTF_VBE("Video Mode 0x%04x available, %s\n",
626 mode_info.video_mode,
627 (mode_info.attributes & 0x1) ==
628 0 ? "not supported" : "supported");
629 DEBUG_PRINTF_VBE("\tTTY: %s\n",
630 (mode_info.attributes & 0x4) ==
632 DEBUG_PRINTF_VBE("\tMode: %s %s\n",
633 (mode_info.attributes & 0x8) ==
634 0 ? "monochrome" : "color",
635 (mode_info.attributes & 0x10) ==
636 0 ? "text" : "graphics");
637 DEBUG_PRINTF_VBE("\tVGA: %s\n",
638 (mode_info.attributes & 0x20) ==
639 0 ? "compatible" : "not compatible");
640 DEBUG_PRINTF_VBE("\tWindowed Mode: %s\n",
641 (mode_info.attributes & 0x40) ==
643 DEBUG_PRINTF_VBE("\tFramebuffer: %s\n",
644 (mode_info.attributes & 0x80) ==
646 DEBUG_PRINTF_VBE("\tResolution: %dx%d\n",
647 mode_info.x_resolution,
648 mode_info.y_resolution);
649 DEBUG_PRINTF_VBE("\tChar Size: %dx%d\n",
650 mode_info.x_charsize, mode_info.y_charsize);
651 DEBUG_PRINTF_VBE("\tColor Depth: %dbpp\n",
652 mode_info.bits_per_pixel);
653 DEBUG_PRINTF_VBE("\tMemory Model: 0x%x\n",
654 mode_info.memory_model);
655 DEBUG_PRINTF_VBE("\tFramebuffer Offset: %08x\n",
656 mode_info.framebuffer_address);
658 if ((mode_info.bits_per_pixel == input.color_depth)
659 && (mode_info.x_resolution <= input.max_screen_width)
660 && ((mode_info.attributes & 0x80) != 0) // framebuffer mode
661 && ((mode_info.attributes & 0x10) != 0) // graphics
662 && ((mode_info.attributes & 0x8) != 0) // color
663 && (mode_info.x_resolution > best_mode_info.x_resolution)) // better than previous best_mode
665 // yiiiihaah... we found a new best mode
666 memcpy(&best_mode_info, &mode_info, sizeof(mode_info));
671 if (best_mode_info.video_mode != 0) {
673 ("Best Video Mode found: 0x%x, %dx%d, %dbpp, framebuffer_address: 0x%x\n",
674 best_mode_info.video_mode,
675 best_mode_info.x_resolution,
676 best_mode_info.y_resolution,
677 best_mode_info.bits_per_pixel,
678 best_mode_info.framebuffer_address);
680 //printf("Mode Info Dump:");
681 //dump(best_mode_info.mode_info_block, 64);
683 // set the video mode
684 vbe_set_mode(&best_mode_info);
686 if ((info.capabilities & 0x1) != 0) {
687 // switch to 8 bit palette format
688 vbe_set_palette_format(8);
691 // - first 216 colors are mixed colors for each component in 6 steps
693 // - then 10 shades of the three primary colors
694 // - then 10 shades of grey
698 // - finally black is color 0 and white color FF (because SLOF expects it
700 // this resembles the palette that the kernel/X Server seems to expect...
702 uint8_t mixed_color_values[6] =
703 { 0xFF, 0xDA, 0xB3, 0x87, 0x54, 0x00 };
704 uint8_t primary_color_values[10] =
705 { 0xF3, 0xE7, 0xCD, 0xC0, 0xA5, 0x96, 0x77, 0x66, 0x3F,
708 uint8_t mc_size = sizeof(mixed_color_values);
709 uint8_t prim_size = sizeof(primary_color_values);
711 uint8_t curr_color_index;
716 for (r = 0; r < mc_size; r++) {
717 for (g = 0; g < mc_size; g++) {
718 for (b = 0; b < mc_size; b++) {
720 (r * mc_size * mc_size) +
723 curr_color |= ((uint32_t) mixed_color_values[r]) << 16; //red value
724 curr_color |= ((uint32_t) mixed_color_values[g]) << 8; //green value
725 curr_color |= (uint32_t) mixed_color_values[b]; //blue value
726 vbe_set_color(curr_color_index,
732 // 10 shades of each primary color
734 for (r = 0; r < prim_size; r++) {
735 curr_color_index = mc_size * mc_size * mc_size + r;
736 curr_color = ((uint32_t) primary_color_values[r]) << 16;
737 vbe_set_color(curr_color_index, curr_color);
740 for (g = 0; g < prim_size; g++) {
742 mc_size * mc_size * mc_size + prim_size + g;
743 curr_color = ((uint32_t) primary_color_values[g]) << 8;
744 vbe_set_color(curr_color_index, curr_color);
747 for (b = 0; b < prim_size; b++) {
749 mc_size * mc_size * mc_size + prim_size * 2 + b;
750 curr_color = (uint32_t) primary_color_values[b];
751 vbe_set_color(curr_color_index, curr_color);
754 for (i = 0; i < prim_size; i++) {
756 mc_size * mc_size * mc_size + prim_size * 3 + i;
758 curr_color |= ((uint32_t) primary_color_values[i]) << 16; //red
759 curr_color |= ((uint32_t) primary_color_values[i]) << 8; //green
760 curr_color |= ((uint32_t) primary_color_values[i]); //blue
761 vbe_set_color(curr_color_index, curr_color);
764 // SLOF is using color 0x0 (black) and 0xFF (white) to draw to the screen...
765 vbe_set_color(0x00, 0x00000000);
766 vbe_set_color(0xFF, 0x00FFFFFF);
768 output->screen_width = best_mode_info.x_resolution;
769 output->screen_height = best_mode_info.y_resolution;
770 output->screen_linebytes = best_mode_info.linebytes;
771 output->color_depth = best_mode_info.bits_per_pixel;
772 output->framebuffer_address =
773 best_mode_info.framebuffer_address;
775 printf("%s: No suitable video mode found!\n", __FUNCTION__);
776 //unset display_type...
777 output->display_type = 0;