1 /******************************************************************************
2 * Copyright (c) 2004, 2008 IBM Corporation
3 * Copyright (c) 2009 Pattrick Hueper <phueper@hueper.net>
5 * This program and the accompanying materials
6 * are made available under the terms of the BSD License
7 * which accompanies this distribution, and is available at
8 * http://www.opensource.org/licenses/bsd-license.php
11 * IBM Corporation - initial implementation
12 *****************************************************************************/
19 #include <x86emu/x86emu.h>
20 #include <x86emu/regs.h>
21 #include "../x86emu/prim_ops.h"
26 #include "interrupt.h"
29 static X86EMU_memFuncs my_mem_funcs = {
30 my_rdb, my_rdw, my_rdl,
31 my_wrb, my_wrw, my_wrl
34 static X86EMU_pioFuncs my_pio_funcs = {
35 my_inb, my_inw, my_inl,
36 my_outb, my_outw, my_outl
39 // pointer to VBEInfoBuffer, set by vbe_prepare
40 u8 *vbe_info_buffer = 0;
41 // virtual BIOS Memory
45 // these structs are for input from and output to OF
47 u8 display_type; // 0=NONE, 1= analog, 2=digital
50 u16 screen_linebytes; // bytes per line in framebuffer, may be more than screen_width
51 u8 color_depth; // color depth in bpp
52 u32 framebuffer_address;
53 u8 edid_block_zero[128];
54 } __attribute__ ((__packed__)) screen_info_t;
62 } __attribute__ ((__packed__)) screen_info_input_t;
64 // these structs only store a subset of the VBE defined fields
71 u16 video_mode_list[256]; // lets hope we never have more than 256 video modes...
77 u8 mode_info_block[256];
86 u32 framebuffer_address;
90 u8 port_number; // i.e. monitor number
91 u8 edid_transfer_time;
93 u8 edid_block_zero[128];
99 vbe_info_buffer = biosmem + (VBE_SEGMENT << 4); // segment:offset off VBE Data Area
101 memset(vbe_info_buffer, 0, 512);
102 //set VbeSignature to "VBE2" to indicate VBE 2.0+ request
103 vbe_info_buffer[0] = 'V';
104 vbe_info_buffer[0] = 'B';
105 vbe_info_buffer[0] = 'E';
106 vbe_info_buffer[0] = '2';
107 // ES:DI store pointer to buffer in virtual mem see vbe_info_buffer above...
109 M.x86.R_ES = VBE_SEGMENT;
111 return 0; // successfull init
116 vbe_info(vbe_info_t * info)
119 // call VBE function 00h (Info Function)
120 M.x86.R_EAX = 0x4f00;
123 CHECK_DBG(DEBUG_TRACE_X86EMU) {
126 // run VESA Interrupt
129 if (M.x86.R_AL != 0x4f) {
130 DEBUG_PRINTF_VBE("%s: VBE Info Function NOT supported! AL=%x\n",
131 __func__, M.x86.R_AL);
135 if (M.x86.R_AH != 0x0) {
137 ("%s: VBE Info Function Return Code NOT OK! AH=%x\n",
138 __func__, M.x86.R_AH);
141 //printf("VBE Info Dump:");
142 //dump(vbe_info_buffer, 64);
144 //offset 0: signature
145 info->signature[0] = vbe_info_buffer[0];
146 info->signature[1] = vbe_info_buffer[1];
147 info->signature[2] = vbe_info_buffer[2];
148 info->signature[3] = vbe_info_buffer[3];
150 // offset 4: 16bit le containing VbeVersion
151 info->version = in16le(vbe_info_buffer + 4);
153 // offset 6: 32bit le containg segment:offset of OEM String in virtual Mem.
154 info->oem_string_ptr =
155 biosmem + ((in16le(vbe_info_buffer + 8) << 4) +
156 in16le(vbe_info_buffer + 6));
158 // offset 10: 32bit le capabilities
159 info->capabilities = in32le(vbe_info_buffer + 10);
161 // offset 14: 32 bit le containing segment:offset of supported video mode table
165 ((in16le(vbe_info_buffer + 16) << 4) +
166 in16le(vbe_info_buffer + 14)));
169 info->video_mode_list[i] = in16le(video_mode_ptr + i);
173 (sizeof(info->video_mode_list) /
174 sizeof(info->video_mode_list[0])))
175 && (info->video_mode_list[i - 1] != 0xFFFF));
177 //offset 18: 16bit le total memory in 64KB blocks
178 info->total_memory = in16le(vbe_info_buffer + 18);
185 vbe_get_mode_info(vbe_mode_info_t * mode_info)
188 // call VBE function 01h (Return VBE Mode Info Function)
189 M.x86.R_EAX = 0x4f01;
190 M.x86.R_CX = mode_info->video_mode;
193 CHECK_DBG(DEBUG_TRACE_X86EMU) {
196 // run VESA Interrupt
199 if (M.x86.R_AL != 0x4f) {
201 ("%s: VBE Return Mode Info Function NOT supported! AL=%x\n",
202 __func__, M.x86.R_AL);
206 if (M.x86.R_AH != 0x0) {
208 ("%s: VBE Return Mode Info (mode: %04x) Function Return Code NOT OK! AH=%02x\n",
209 __func__, mode_info->video_mode, M.x86.R_AH);
212 //pointer to mode_info_block is in ES:DI
213 memcpy(mode_info->mode_info_block,
214 biosmem + ((M.x86.R_ES << 4) + M.x86.R_DI),
215 sizeof(mode_info->mode_info_block));
217 //printf("Mode Info Dump:");
218 //dump(mode_info_block, 64);
220 // offset 0: 16bit le mode attributes
221 mode_info->attributes = in16le(mode_info->mode_info_block);
223 // offset 16: 16bit le bytes per scan line
224 mode_info->linebytes = in16le(mode_info->mode_info_block + 16);
226 // offset 18: 16bit le x resolution
227 mode_info->x_resolution = in16le(mode_info->mode_info_block + 18);
229 // offset 20: 16bit le y resolution
230 mode_info->y_resolution = in16le(mode_info->mode_info_block + 20);
232 // offset 22: 8bit le x charsize
233 mode_info->x_charsize = *(mode_info->mode_info_block + 22);
235 // offset 23: 8bit le y charsize
236 mode_info->y_charsize = *(mode_info->mode_info_block + 23);
238 // offset 25: 8bit le bits per pixel
239 mode_info->bits_per_pixel = *(mode_info->mode_info_block + 25);
241 // offset 27: 8bit le memory model
242 mode_info->memory_model = *(mode_info->mode_info_block + 27);
244 // offset 40: 32bit le containg offset of frame buffer memory ptr
245 mode_info->framebuffer_address =
246 in32le(mode_info->mode_info_block + 40);
253 vbe_set_mode(vbe_mode_info_t * mode_info)
256 // call VBE function 02h (Set VBE Mode Function)
257 M.x86.R_EAX = 0x4f02;
258 M.x86.R_BX = mode_info->video_mode;
259 M.x86.R_BX |= 0x4000; // set bit 14 to request linear framebuffer mode
260 M.x86.R_BX &= 0x7FFF; // clear bit 15 to request clearing of framebuffer
262 DEBUG_PRINTF_VBE("%s: setting mode: 0x%04x\n", __func__,
266 CHECK_DBG(DEBUG_TRACE_X86EMU) {
269 // run VESA Interrupt
272 if (M.x86.R_AL != 0x4f) {
274 ("%s: VBE Set Mode Function NOT supported! AL=%x\n",
275 __func__, M.x86.R_AL);
279 if (M.x86.R_AH != 0x0) {
281 ("%s: mode: %x VBE Set Mode Function Return Code NOT OK! AH=%x\n",
282 __func__, mode_info->video_mode, M.x86.R_AH);
290 vbe_set_palette_format(u8 format)
293 // call VBE function 09h (Set/Get Palette Data Function)
294 M.x86.R_EAX = 0x4f08;
295 M.x86.R_BL = 0x00; // set format
298 DEBUG_PRINTF_VBE("%s: setting palette format: %d\n", __func__,
302 CHECK_DBG(DEBUG_TRACE_X86EMU) {
305 // run VESA Interrupt
308 if (M.x86.R_AL != 0x4f) {
310 ("%s: VBE Set Palette Format Function NOT supported! AL=%x\n",
311 __func__, M.x86.R_AL);
315 if (M.x86.R_AH != 0x0) {
317 ("%s: VBE Set Palette Format Function Return Code NOT OK! AH=%x\n",
318 __func__, M.x86.R_AH);
326 vbe_set_color(u16 color_number, u32 color_value)
329 // call VBE function 09h (Set/Get Palette Data Function)
330 M.x86.R_EAX = 0x4f09;
331 M.x86.R_BL = 0x00; // set color
332 M.x86.R_CX = 0x01; // set only one entry
333 M.x86.R_DX = color_number;
334 // ES:DI is address where color_value is stored, we store it at 2000:0000
338 // store color value at ES:DI
339 out32le(biosmem + (M.x86.R_ES << 4) + M.x86.R_DI, color_value);
341 DEBUG_PRINTF_VBE("%s: setting color #%x: 0x%04x\n", __func__,
342 color_number, color_value);
345 CHECK_DBG(DEBUG_TRACE_X86EMU) {
348 // run VESA Interrupt
351 if (M.x86.R_AL != 0x4f) {
353 ("%s: VBE Set Palette Function NOT supported! AL=%x\n",
354 __func__, M.x86.R_AL);
358 if (M.x86.R_AH != 0x0) {
360 ("%s: VBE Set Palette Function Return Code NOT OK! AH=%x\n",
361 __func__, M.x86.R_AH);
368 vbe_get_color(u16 color_number, u32 * color_value)
371 // call VBE function 09h (Set/Get Palette Data Function)
372 M.x86.R_EAX = 0x4f09;
373 M.x86.R_BL = 0x00; // get color
374 M.x86.R_CX = 0x01; // get only one entry
375 M.x86.R_DX = color_number;
376 // ES:DI is address where color_value is stored, we store it at 2000:0000
381 CHECK_DBG(DEBUG_TRACE_X86EMU) {
384 // run VESA Interrupt
387 if (M.x86.R_AL != 0x4f) {
389 ("%s: VBE Set Palette Function NOT supported! AL=%x\n",
390 __func__, M.x86.R_AL);
394 if (M.x86.R_AH != 0x0) {
396 ("%s: VBE Set Palette Function Return Code NOT OK! AH=%x\n",
397 __func__, M.x86.R_AH);
400 // read color value from ES:DI
401 *color_value = in32le(biosmem + (M.x86.R_ES << 4) + M.x86.R_DI);
403 DEBUG_PRINTF_VBE("%s: getting color #%x --> 0x%04x\n", __func__,
404 color_number, *color_value);
411 vbe_get_ddc_info(vbe_ddc_info_t * ddc_info)
414 // call VBE function 15h (DDC Info Function)
415 M.x86.R_EAX = 0x4f15;
416 M.x86.R_BL = 0x00; // get DDC Info
417 M.x86.R_CX = ddc_info->port_number;
422 CHECK_DBG(DEBUG_TRACE_X86EMU) {
425 // run VESA Interrupt
428 if (M.x86.R_AL != 0x4f) {
430 ("%s: VBE Get DDC Info Function NOT supported! AL=%x\n",
431 __func__, M.x86.R_AL);
435 if (M.x86.R_AH != 0x0) {
437 ("%s: port: %x VBE Get DDC Info Function Return Code NOT OK! AH=%x\n",
438 __func__, ddc_info->port_number, M.x86.R_AH);
441 // BH = approx. time in seconds to transfer one EDID block
442 ddc_info->edid_transfer_time = M.x86.R_BH;
444 ddc_info->ddc_level = M.x86.R_BL;
447 // call VBE function 15h (DDC Info Function)
448 M.x86.R_EAX = 0x4f15;
449 M.x86.R_BL = 0x01; // read EDID
450 M.x86.R_CX = ddc_info->port_number;
451 M.x86.R_DX = 0x0; // block number
452 // ES:DI is address where EDID is stored, we store it at 2000:0000
457 CHECK_DBG(DEBUG_TRACE_X86EMU) {
460 // run VESA Interrupt
463 if (M.x86.R_AL != 0x4f) {
465 ("%s: VBE Read EDID Function NOT supported! AL=%x\n",
466 __func__, M.x86.R_AL);
470 if (M.x86.R_AH != 0x0) {
472 ("%s: port: %x VBE Read EDID Function Return Code NOT OK! AH=%x\n",
473 __func__, ddc_info->port_number, M.x86.R_AH);
477 memcpy(ddc_info->edid_block_zero,
478 biosmem + (M.x86.R_ES << 4) + M.x86.R_DI,
479 sizeof(ddc_info->edid_block_zero));
485 vbe_get_info(u8 argc, char ** argv)
491 ("Usage %s <vmem_base> <device_path> <address of screen_info_t>\n",
494 for (i = 0; i < argc; i++) {
495 printf("argv[%d]: %s\n", i, argv[i]);
499 // get a copy of input struct...
500 screen_info_input_t input =
501 *((screen_info_input_t *) strtoul((char *) argv[4], 0, 16));
502 // output is pointer to the address passed as argv[4]
503 screen_info_t *output =
504 (screen_info_t *) strtoul((char *) argv[4], 0, 16);
506 memset(output, 0, sizeof(screen_info_t));
508 // argv[1] is address of virtual BIOS mem...
509 // argv[2] is the size
510 biosmem = (u8 *) strtoul(argv[1], 0, 16);
511 biosmem_size = strtoul(argv[2], 0, 16);;
512 if (biosmem_size < MIN_REQUIRED_VMEM_SIZE) {
513 printf("Error: Not enough virtual memory: %x, required: %x!\n",
514 biosmem_size, MIN_REQUIRED_VMEM_SIZE);
517 // argv[3] is the device to open and use...
518 if (dev_init((char *) argv[3]) != 0) {
519 printf("Error initializing device!\n");
522 //setup interrupt handler
523 X86EMU_intrFuncs intrFuncs[256];
524 for (i = 0; i < 256; i++)
525 intrFuncs[i] = handleInterrupt;
526 X86EMU_setupIntrFuncs(intrFuncs);
527 X86EMU_setupPioFuncs(&my_pio_funcs);
528 X86EMU_setupMemFuncs(&my_mem_funcs);
531 M.mem_base = (long) biosmem;
532 M.mem_size = biosmem_size;
533 DEBUG_PRINTF_VBE("membase set: %08x, size: %08x\n", (int) M.mem_base,
537 rval = vbe_info(&info);
541 DEBUG_PRINTF_VBE("VbeSignature: %s\n", info.signature);
542 DEBUG_PRINTF_VBE("VbeVersion: 0x%04x\n", info.version);
543 DEBUG_PRINTF_VBE("OemString: %s\n", info.oem_string_ptr);
544 DEBUG_PRINTF_VBE("Capabilities:\n");
545 DEBUG_PRINTF_VBE("\tDAC: %s\n",
546 (info.capabilities & 0x1) ==
547 0 ? "fixed 6bit" : "switchable 6/8bit");
548 DEBUG_PRINTF_VBE("\tVGA: %s\n",
549 (info.capabilities & 0x2) ==
550 0 ? "compatible" : "not compatible");
551 DEBUG_PRINTF_VBE("\tRAMDAC: %s\n",
552 (info.capabilities & 0x4) ==
553 0 ? "normal" : "use blank bit in Function 09h");
555 // argv[4] may be a pointer with enough space to return screen_info_t
556 // as input, it must contain a screen_info_input_t with the following content:
557 // byte[0:3] = "DDC\0" (zero-terminated signature header)
558 // byte[4:5] = reserved space for the return struct... just in case we ever change
559 // the struct and dont have reserved enough memory (and let's hope the struct
560 // never gets larger than 64KB)
561 // byte[6] = monitor port number for DDC requests ("only" one byte... so lets hope we never have more than 255 monitors...
562 // byte[7:8] = max. screen width (OF may want to limit this)
563 // byte[9] = required color depth in bpp
564 if (strncmp((char *) input.signature, "DDC", 4) != 0) {
566 ("%s: Invalid input signature! expected: %s, is: %s\n",
567 __func__, "DDC", input.signature);
570 if (input.size_reserved != sizeof(screen_info_t)) {
572 ("%s: Size of return struct is wrong, required: %d, available: %d\n",
573 __func__, (int) sizeof(screen_info_t),
574 input.size_reserved);
578 vbe_ddc_info_t ddc_info;
579 ddc_info.port_number = input.monitor_number;
580 vbe_get_ddc_info(&ddc_info);
583 DEBUG_PRINTF_VBE("DDC: edid_tranfer_time: %d\n",
584 ddc_info.edid_transfer_time);
585 DEBUG_PRINTF_VBE("DDC: ddc_level: %x\n", ddc_info.ddc_level);
586 DEBUG_PRINTF_VBE("DDC: EDID: \n");
587 CHECK_DBG(DEBUG_VBE) {
588 dump(ddc_info.edid_block_zero,
589 sizeof(ddc_info.edid_block_zero));
592 if (*((u64 *) ddc_info.edid_block_zero) !=
593 (u64) 0x00FFFFFFFFFFFF00) {
594 // invalid EDID signature... probably no monitor
596 output->display_type = 0x0;
598 } else if ((ddc_info.edid_block_zero[20] & 0x80) != 0) {
600 output->display_type = 2;
603 output->display_type = 1;
605 DEBUG_PRINTF_VBE("DDC: found display type %d\n", output->display_type);
606 memcpy(output->edid_block_zero, ddc_info.edid_block_zero,
607 sizeof(ddc_info.edid_block_zero));
609 vbe_mode_info_t mode_info;
610 vbe_mode_info_t best_mode_info;
611 // initialize best_mode to 0
612 memset(&best_mode_info, 0, sizeof(best_mode_info));
613 while ((mode_info.video_mode = info.video_mode_list[i]) != 0xFFFF) {
614 //DEBUG_PRINTF_VBE("%x: Mode: %04x\n", i, mode_info.video_mode);
615 vbe_get_mode_info(&mode_info);
617 DEBUG_PRINTF_VBE("Video Mode 0x%04x available, %s\n",
618 mode_info.video_mode,
619 (mode_info.attributes & 0x1) ==
620 0 ? "not supported" : "supported");
621 DEBUG_PRINTF_VBE("\tTTY: %s\n",
622 (mode_info.attributes & 0x4) ==
624 DEBUG_PRINTF_VBE("\tMode: %s %s\n",
625 (mode_info.attributes & 0x8) ==
626 0 ? "monochrome" : "color",
627 (mode_info.attributes & 0x10) ==
628 0 ? "text" : "graphics");
629 DEBUG_PRINTF_VBE("\tVGA: %s\n",
630 (mode_info.attributes & 0x20) ==
631 0 ? "compatible" : "not compatible");
632 DEBUG_PRINTF_VBE("\tWindowed Mode: %s\n",
633 (mode_info.attributes & 0x40) ==
635 DEBUG_PRINTF_VBE("\tFramebuffer: %s\n",
636 (mode_info.attributes & 0x80) ==
638 DEBUG_PRINTF_VBE("\tResolution: %dx%d\n",
639 mode_info.x_resolution,
640 mode_info.y_resolution);
641 DEBUG_PRINTF_VBE("\tChar Size: %dx%d\n",
642 mode_info.x_charsize, mode_info.y_charsize);
643 DEBUG_PRINTF_VBE("\tColor Depth: %dbpp\n",
644 mode_info.bits_per_pixel);
645 DEBUG_PRINTF_VBE("\tMemory Model: 0x%x\n",
646 mode_info.memory_model);
647 DEBUG_PRINTF_VBE("\tFramebuffer Offset: %08x\n",
648 mode_info.framebuffer_address);
650 if ((mode_info.bits_per_pixel == input.color_depth)
651 && (mode_info.x_resolution <= input.max_screen_width)
652 && ((mode_info.attributes & 0x80) != 0) // framebuffer mode
653 && ((mode_info.attributes & 0x10) != 0) // graphics
654 && ((mode_info.attributes & 0x8) != 0) // color
655 && (mode_info.x_resolution > best_mode_info.x_resolution)) // better than previous best_mode
657 // yiiiihaah... we found a new best mode
658 memcpy(&best_mode_info, &mode_info, sizeof(mode_info));
663 if (best_mode_info.video_mode != 0) {
665 ("Best Video Mode found: 0x%x, %dx%d, %dbpp, framebuffer_address: 0x%x\n",
666 best_mode_info.video_mode,
667 best_mode_info.x_resolution,
668 best_mode_info.y_resolution,
669 best_mode_info.bits_per_pixel,
670 best_mode_info.framebuffer_address);
672 //printf("Mode Info Dump:");
673 //dump(best_mode_info.mode_info_block, 64);
675 // set the video mode
676 vbe_set_mode(&best_mode_info);
678 if ((info.capabilities & 0x1) != 0) {
679 // switch to 8 bit palette format
680 vbe_set_palette_format(8);
683 // - first 216 colors are mixed colors for each component in 6 steps
685 // - then 10 shades of the three primary colors
686 // - then 10 shades of grey
690 // - finally black is color 0 and white color FF (because SLOF expects it
692 // this resembles the palette that the kernel/X Server seems to expect...
694 u8 mixed_color_values[6] =
695 { 0xFF, 0xDA, 0xB3, 0x87, 0x54, 0x00 };
696 u8 primary_color_values[10] =
697 { 0xF3, 0xE7, 0xCD, 0xC0, 0xA5, 0x96, 0x77, 0x66, 0x3F,
700 u8 mc_size = sizeof(mixed_color_values);
701 u8 prim_size = sizeof(primary_color_values);
708 for (r = 0; r < mc_size; r++) {
709 for (g = 0; g < mc_size; g++) {
710 for (b = 0; b < mc_size; b++) {
712 (r * mc_size * mc_size) +
715 curr_color |= ((u32) mixed_color_values[r]) << 16; //red value
716 curr_color |= ((u32) mixed_color_values[g]) << 8; //green value
717 curr_color |= (u32) mixed_color_values[b]; //blue value
718 vbe_set_color(curr_color_index,
724 // 10 shades of each primary color
726 for (r = 0; r < prim_size; r++) {
727 curr_color_index = mc_size * mc_size * mc_size + r;
728 curr_color = ((u32) primary_color_values[r]) << 16;
729 vbe_set_color(curr_color_index, curr_color);
732 for (g = 0; g < prim_size; g++) {
734 mc_size * mc_size * mc_size + prim_size + g;
735 curr_color = ((u32) primary_color_values[g]) << 8;
736 vbe_set_color(curr_color_index, curr_color);
739 for (b = 0; b < prim_size; b++) {
741 mc_size * mc_size * mc_size + prim_size * 2 + b;
742 curr_color = (u32) primary_color_values[b];
743 vbe_set_color(curr_color_index, curr_color);
746 for (i = 0; i < prim_size; i++) {
748 mc_size * mc_size * mc_size + prim_size * 3 + i;
750 curr_color |= ((u32) primary_color_values[i]) << 16; //red
751 curr_color |= ((u32) primary_color_values[i]) << 8; //green
752 curr_color |= ((u32) primary_color_values[i]); //blue
753 vbe_set_color(curr_color_index, curr_color);
756 // SLOF is using color 0x0 (black) and 0xFF (white) to draw to the screen...
757 vbe_set_color(0x00, 0x00000000);
758 vbe_set_color(0xFF, 0x00FFFFFF);
760 output->screen_width = best_mode_info.x_resolution;
761 output->screen_height = best_mode_info.y_resolution;
762 output->screen_linebytes = best_mode_info.linebytes;
763 output->color_depth = best_mode_info.bits_per_pixel;
764 output->framebuffer_address =
765 best_mode_info.framebuffer_address;
767 printf("%s: No suitable video mode found!\n", __func__);
768 //unset display_type...
769 output->display_type = 0;