forked from Minki/linux
1722770f13
The new x86 setup code (4fd06960f1
) broke booting on an old P3/500MHz
with an onboard Voodoo3 of mine. After debugging it, it turned out
to be caused by the fact that the vesa probing now asks for VBE2 data.
Disassembing the video BIOS shows that it overflows the vesa_general_info
structure when VBE2 data is requested because the source addresses for the
information strings which get strcpy'ed to the buffer lie outside the 32K
BIOS code (and hence contain long sequences of 0xff's).
E.G.:
get_vbe_controller_info:
00002A9C 60 pushaw
00002A9D 1E push ds
00002A9E 0E push cs
00002A9F 1F pop ds
00002AA0 2BC9 sub cx,cx
00002AA2 6626813D56424532 cmp dword [es:di],0x32454256 ; "VBE2"
00002AAA 7501 jnz .1
00002AAC 41 inc cx
.1:
00002AAD 51 push cx
00002AAE B91400 mov cx,0x14
00002AB1 BED47F mov si, controller_header
00002AB4 57 push di
00002AB5 F3A4 rep movsb ; copy vbe1.2 header
00002AB7 B9EC00 mov cx,0xec
00002ABA 2AC0 sub al,al
00002ABC F3AA rep stosb ; zero pad remainder
00002ABE 5F pop di
00002ABF E8EB0D call word get_memory
00002AC2 C1E002 shl ax,0x2
00002AC5 26894512 mov [es:di+0x12],ax ; total memory
00002AC9 26C745040003 mov word [es:di+0x4],0x300 ; VBE version
00002ACF 268C4D08 mov [es:di+0x8],cs
00002AD3 268C4D10 mov [es:di+0x10],cs
00002AD7 59 pop cx
00002AD8 E361 jcxz .done ; VBE2 requested?
00002ADA 8D9D0001 lea bx,[di+0x100]
00002ADE 53 push bx
00002ADF 87DF xchg bx,di ; di now points to 2nd half
00002AE1 26C747140001 mov word [es:bx+0x14],0x100 ; sw rev
00002AE7 26897F06 mov [es:bx+0x6],di ; oem string
00002AEB 268C4708 mov [es:bx+0x8],es
00002AEF BE5280 mov si,0x8052 ; oem string
00002AF2 E87A1B call word strcpy
00002AF5 26897F0E mov [es:bx+0xe],di ; video mode list
00002AF9 268C4710 mov [es:bx+0x10],es
00002AFD B91E00 mov cx,0x1e
00002B00 BEE87F mov si,vidmodes
00002B03 F3A5 rep movsw
00002B05 26897F16 mov [es:bx+0x16],di ; oem vendor
00002B09 268C4718 mov [es:bx+0x18],es
00002B0D BE2480 mov si,0x8024 ; oem vendor
00002B10 E85C1B call word strcpy
00002B13 26897F1A mov [es:bx+0x1a],di ; oem product
00002B17 268C471C mov [es:bx+0x1c],es
00002B1B BE3880 mov si,0x8038 ; oem product
00002B1E E84E1B call word strcpy
00002B21 26897F1E mov [es:bx+0x1e],di ; oem product rev
00002B25 268C4720 mov [es:bx+0x20],es
00002B29 BE4580 mov si,0x8045 ; oem product rev
00002B2C E8401B call word strcpy
00002B2F 58 pop ax
00002B30 B90001 mov cx,0x100
00002B33 2BCF sub cx,di
00002B35 03C8 add cx,ax
00002B37 2AC0 sub al,al
00002B39 F3AA rep stosb ; zero pad
.done:
00002B3B 1F pop ds
00002B3C 61 popaw
00002B3D B84F00 mov ax,0x4f
00002B40 C3 ret
(The full BIOS can be found at http://peter.korsgaard.com/vgabios.bin
if interested).
The old setup code didn't ask for VBE2 info, and the new code doesn't
actually do anything with the extra information, so the fix is to simply
not request it. Other BIOS'es might have the same problem.
Signed-off-by: Peter Korsgaard <jacmet@sunsite.dk>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
299 lines
7.1 KiB
C
299 lines
7.1 KiB
C
/* -*- linux-c -*- ------------------------------------------------------- *
|
|
*
|
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
|
* Copyright 2007 rPath, Inc. - All Rights Reserved
|
|
*
|
|
* This file is part of the Linux kernel, and is made available under
|
|
* the terms of the GNU General Public License version 2.
|
|
*
|
|
* ----------------------------------------------------------------------- */
|
|
|
|
/*
|
|
* arch/i386/boot/video-vesa.c
|
|
*
|
|
* VESA text modes
|
|
*/
|
|
|
|
#include "boot.h"
|
|
#include "video.h"
|
|
#include "vesa.h"
|
|
|
|
/* VESA information */
|
|
static struct vesa_general_info vginfo;
|
|
static struct vesa_mode_info vminfo;
|
|
|
|
__videocard video_vesa;
|
|
|
|
static void vesa_store_mode_params_graphics(void);
|
|
|
|
static int vesa_probe(void)
|
|
{
|
|
#if defined(CONFIG_VIDEO_VESA) || defined(CONFIG_FIRMWARE_EDID)
|
|
u16 ax, cx, di;
|
|
u16 mode;
|
|
addr_t mode_ptr;
|
|
struct mode_info *mi;
|
|
int nmodes = 0;
|
|
|
|
video_vesa.modes = GET_HEAP(struct mode_info, 0);
|
|
|
|
ax = 0x4f00;
|
|
di = (size_t)&vginfo;
|
|
asm(INT10
|
|
: "+a" (ax), "+D" (di), "=m" (vginfo)
|
|
: : "ebx", "ecx", "edx", "esi");
|
|
|
|
if (ax != 0x004f ||
|
|
vginfo.signature != VESA_MAGIC ||
|
|
vginfo.version < 0x0102)
|
|
return 0; /* Not present */
|
|
#endif /* CONFIG_VIDEO_VESA || CONFIG_FIRMWARE_EDID */
|
|
#ifdef CONFIG_VIDEO_VESA
|
|
set_fs(vginfo.video_mode_ptr.seg);
|
|
mode_ptr = vginfo.video_mode_ptr.off;
|
|
|
|
while ((mode = rdfs16(mode_ptr)) != 0xffff) {
|
|
mode_ptr += 2;
|
|
|
|
if (!heap_free(sizeof(struct mode_info)))
|
|
break; /* Heap full, can't save mode info */
|
|
|
|
if (mode & ~0x1ff)
|
|
continue;
|
|
|
|
memset(&vminfo, 0, sizeof vminfo); /* Just in case... */
|
|
|
|
ax = 0x4f01;
|
|
cx = mode;
|
|
di = (size_t)&vminfo;
|
|
asm(INT10
|
|
: "+a" (ax), "+c" (cx), "+D" (di), "=m" (vminfo)
|
|
: : "ebx", "edx", "esi");
|
|
|
|
if (ax != 0x004f)
|
|
continue;
|
|
|
|
if ((vminfo.mode_attr & 0x15) == 0x05) {
|
|
/* Text Mode, TTY BIOS supported,
|
|
supported by hardware */
|
|
mi = GET_HEAP(struct mode_info, 1);
|
|
mi->mode = mode + VIDEO_FIRST_VESA;
|
|
mi->depth = 0; /* text */
|
|
mi->x = vminfo.h_res;
|
|
mi->y = vminfo.v_res;
|
|
nmodes++;
|
|
} else if ((vminfo.mode_attr & 0x99) == 0x99 &&
|
|
(vminfo.memory_layout == 4 ||
|
|
vminfo.memory_layout == 6) &&
|
|
vminfo.memory_planes == 1) {
|
|
#ifdef CONFIG_FB
|
|
/* Graphics mode, color, linear frame buffer
|
|
supported. Only register the mode if
|
|
if framebuffer is configured, however,
|
|
otherwise the user will be left without a screen.
|
|
We don't require CONFIG_FB_VESA, however, since
|
|
some of the other framebuffer drivers can use
|
|
this mode-setting, too. */
|
|
mi = GET_HEAP(struct mode_info, 1);
|
|
mi->mode = mode + VIDEO_FIRST_VESA;
|
|
mi->depth = vminfo.bpp;
|
|
mi->x = vminfo.h_res;
|
|
mi->y = vminfo.v_res;
|
|
nmodes++;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return nmodes;
|
|
#else
|
|
return 0;
|
|
#endif /* CONFIG_VIDEO_VESA */
|
|
}
|
|
|
|
static int vesa_set_mode(struct mode_info *mode)
|
|
{
|
|
u16 ax, bx, cx, di;
|
|
int is_graphic;
|
|
u16 vesa_mode = mode->mode - VIDEO_FIRST_VESA;
|
|
|
|
memset(&vminfo, 0, sizeof vminfo); /* Just in case... */
|
|
|
|
ax = 0x4f01;
|
|
cx = vesa_mode;
|
|
di = (size_t)&vminfo;
|
|
asm(INT10
|
|
: "+a" (ax), "+c" (cx), "+D" (di), "=m" (vminfo)
|
|
: : "ebx", "edx", "esi");
|
|
|
|
if (ax != 0x004f)
|
|
return -1;
|
|
|
|
if ((vminfo.mode_attr & 0x15) == 0x05) {
|
|
/* It's a supported text mode */
|
|
is_graphic = 0;
|
|
} else if ((vminfo.mode_attr & 0x99) == 0x99) {
|
|
/* It's a graphics mode with linear frame buffer */
|
|
is_graphic = 1;
|
|
vesa_mode |= 0x4000; /* Request linear frame buffer */
|
|
} else {
|
|
return -1; /* Invalid mode */
|
|
}
|
|
|
|
|
|
ax = 0x4f02;
|
|
bx = vesa_mode;
|
|
di = 0;
|
|
asm volatile(INT10
|
|
: "+a" (ax), "+b" (bx), "+D" (di)
|
|
: : "ecx", "edx", "esi");
|
|
|
|
if (ax != 0x004f)
|
|
return -1;
|
|
|
|
graphic_mode = is_graphic;
|
|
if (!is_graphic) {
|
|
/* Text mode */
|
|
force_x = mode->x;
|
|
force_y = mode->y;
|
|
do_restore = 1;
|
|
} else {
|
|
/* Graphics mode */
|
|
vesa_store_mode_params_graphics();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Switch DAC to 8-bit mode */
|
|
static void vesa_dac_set_8bits(void)
|
|
{
|
|
u8 dac_size = 6;
|
|
|
|
/* If possible, switch the DAC to 8-bit mode */
|
|
if (vginfo.capabilities & 1) {
|
|
u16 ax, bx;
|
|
|
|
ax = 0x4f08;
|
|
bx = 0x0800;
|
|
asm volatile(INT10
|
|
: "+a" (ax), "+b" (bx)
|
|
: : "ecx", "edx", "esi", "edi");
|
|
|
|
if (ax == 0x004f)
|
|
dac_size = bx >> 8;
|
|
}
|
|
|
|
/* Set the color sizes to the DAC size, and offsets to 0 */
|
|
boot_params.screen_info.red_size = dac_size;
|
|
boot_params.screen_info.green_size = dac_size;
|
|
boot_params.screen_info.blue_size = dac_size;
|
|
boot_params.screen_info.rsvd_size = dac_size;
|
|
|
|
boot_params.screen_info.red_pos = 0;
|
|
boot_params.screen_info.green_pos = 0;
|
|
boot_params.screen_info.blue_pos = 0;
|
|
boot_params.screen_info.rsvd_pos = 0;
|
|
}
|
|
|
|
/* Save the VESA protected mode info */
|
|
static void vesa_store_pm_info(void)
|
|
{
|
|
u16 ax, bx, di, es;
|
|
|
|
ax = 0x4f0a;
|
|
bx = di = 0;
|
|
asm("pushw %%es; "INT10"; movw %%es,%0; popw %%es"
|
|
: "=d" (es), "+a" (ax), "+b" (bx), "+D" (di)
|
|
: : "ecx", "esi");
|
|
|
|
if (ax != 0x004f)
|
|
return;
|
|
|
|
boot_params.screen_info.vesapm_seg = es;
|
|
boot_params.screen_info.vesapm_off = di;
|
|
}
|
|
|
|
/*
|
|
* Save video mode parameters for graphics mode
|
|
*/
|
|
static void vesa_store_mode_params_graphics(void)
|
|
{
|
|
/* Tell the kernel we're in VESA graphics mode */
|
|
boot_params.screen_info.orig_video_isVGA = 0x23;
|
|
|
|
/* Mode parameters */
|
|
boot_params.screen_info.vesa_attributes = vminfo.mode_attr;
|
|
boot_params.screen_info.lfb_linelength = vminfo.logical_scan;
|
|
boot_params.screen_info.lfb_width = vminfo.h_res;
|
|
boot_params.screen_info.lfb_height = vminfo.v_res;
|
|
boot_params.screen_info.lfb_depth = vminfo.bpp;
|
|
boot_params.screen_info.pages = vminfo.image_planes;
|
|
boot_params.screen_info.lfb_base = vminfo.lfb_ptr;
|
|
memcpy(&boot_params.screen_info.red_size,
|
|
&vminfo.rmask, 8);
|
|
|
|
/* General parameters */
|
|
boot_params.screen_info.lfb_size = vginfo.total_memory;
|
|
|
|
if (vminfo.bpp <= 8)
|
|
vesa_dac_set_8bits();
|
|
|
|
vesa_store_pm_info();
|
|
}
|
|
|
|
/*
|
|
* Save EDID information for the kernel; this is invoked, separately,
|
|
* after mode-setting.
|
|
*/
|
|
void vesa_store_edid(void)
|
|
{
|
|
#ifdef CONFIG_FIRMWARE_EDID
|
|
u16 ax, bx, cx, dx, di;
|
|
|
|
/* Apparently used as a nonsense token... */
|
|
memset(&boot_params.edid_info, 0x13, sizeof boot_params.edid_info);
|
|
|
|
if (vginfo.version < 0x0200)
|
|
return; /* EDID requires VBE 2.0+ */
|
|
|
|
ax = 0x4f15; /* VBE DDC */
|
|
bx = 0x0000; /* Report DDC capabilities */
|
|
cx = 0; /* Controller 0 */
|
|
di = 0; /* ES:DI must be 0 by spec */
|
|
|
|
/* Note: The VBE DDC spec is different from the main VESA spec;
|
|
we genuinely have to assume all registers are destroyed here. */
|
|
|
|
asm("pushw %%es; movw %2,%%es; "INT10"; popw %%es"
|
|
: "+a" (ax), "+b" (bx)
|
|
: "c" (cx), "D" (di)
|
|
: "esi");
|
|
|
|
if (ax != 0x004f)
|
|
return; /* No EDID */
|
|
|
|
/* BH = time in seconds to transfer EDD information */
|
|
/* BL = DDC level supported */
|
|
|
|
ax = 0x4f15; /* VBE DDC */
|
|
bx = 0x0001; /* Read EDID */
|
|
cx = 0; /* Controller 0 */
|
|
dx = 0; /* EDID block number */
|
|
di =(size_t) &boot_params.edid_info; /* (ES:)Pointer to block */
|
|
asm(INT10
|
|
: "+a" (ax), "+b" (bx), "+d" (dx), "=m" (boot_params.edid_info)
|
|
: "c" (cx), "D" (di)
|
|
: "esi");
|
|
#endif /* CONFIG_FIRMWARE_EDID */
|
|
}
|
|
|
|
__videocard video_vesa =
|
|
{
|
|
.card_name = "VESA",
|
|
.probe = vesa_probe,
|
|
.set_mode = vesa_set_mode,
|
|
.xmode_first = VIDEO_FIRST_VESA,
|
|
.xmode_n = 0x200,
|
|
};
|