This series add several fixes to coreboot's framebuffer implementation and it main purpose is to fix Microsoft's Windows as supported OS.
The series fixes the following issues: * Windows shows no image in text-mode * Windows NTLDR/bootmgr shows no image in VESA mode * Windows shows no image in VESA mode * Windows legacy Intel VGA driver requires VBT in VGA option ROM * Clear screen broken due to INT 15h replaced by OS
I was able to boot Windows with the following configurations: * using VgaSave driver in text-mode with a fixed display resolution of 640x480 at 4Bpp. * using VgaSave driver in VESA mode and set a display resolution up to 1600x1200 at 24Bpp. * using VgaSave driver in Windows's "Safe Mode" in VESA mode and set a display resolution up to 1600x1200 at 24Bpp. * using the Intel VGA driver with up to the panel native resolution of 1680x1050 at 24Bpp.
Most likely other VESA compatible bootloaders and operating systems will be fixed, too.
Changes since v1: * Add crash detection handler for INT 15h modifications. * Disable VESA modes in cbvga_init * Make memcpy_high method public * Copy Intel VBT to VGA option ROM if provided by ASLS
Patrick Rudolph (8): SeaVGABios/cbvga: Advertise correct pixel format SeaVGABios/cbvga: Assume VGA compatible GPU in text-mode SeaVGABIOS/vbe: Query driver for scanline pitch SeaVGABios/cbvga: Use active mode to clear screen SeaVGABios/cbvga: Advertise compatible VESA modes SeaVGABios/vgafb: Make memcpy_high public SeaVGABios/cbvga:: Add Intel VBT support SeaVGABios/cbvga: Add crash detection handler
vgasrc/bochsvga.c | 5 + vgasrc/bochsvga.h | 1 + vgasrc/cbvga.c | 274 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- vgasrc/clext.c | 5 + vgasrc/stdvga.c | 5 + vgasrc/vbe.c | 2 +- vgasrc/vgaentry.S | 2 +- vgasrc/vgafb.c | 2 +- vgasrc/vgafb.h | 2 +- vgasrc/vgahw.h | 10 ++ vgasrc/vgautil.h | 3 + 11 files changed, 301 insertions(+), 10 deletions(-)
Accumulate the pixel format's bits instead relying on coreboot's bits_per_pixel, which says nothing about the active pixel format.
Allows VBE to correctly advertise XRGB8 and BGRX8 framebuffer formats, commonly used by coreboot.
Signed-off-by: Patrick Rudolph siro@das-labor.org
diff --git a/vgasrc/cbvga.c b/vgasrc/cbvga.c index b8216a9..39ffdbb 100644 --- a/vgasrc/cbvga.c +++ b/vgasrc/cbvga.c @@ -1,6 +1,7 @@ // Simple framebuffer vgabios for use with coreboot native vga init. // // Copyright (C) 2014 Kevin O'Connor kevin@koconnor.net +// Copyright (C) 2017 Patrick Rudolph siro@das-labor.org // // This file may be distributed under the terms of the GNU LGPLv3 license.
@@ -164,7 +165,8 @@ cbvga_setup(void) }
u64 addr = GET_FARVAR(0, cbfb->physical_address); - u8 bpp = GET_FARVAR(0, cbfb->bits_per_pixel); + u8 bpp = cbfb->blue_mask_size + cbfb->green_mask_size + + cbfb->red_mask_size + cbfb->reserved_mask_size; u32 xlines = GET_FARVAR(0, cbfb->x_resolution); u32 ylines = GET_FARVAR(0, cbfb->y_resolution); u32 linelength = GET_FARVAR(0, cbfb->bytes_per_line);
Assume the GPU is VGA compatible when running in text-mode. Advertise VGA modes. Microsoft Windows relies on graphics mode 12 as last resort when running in text mode. It is used even when not advertised.
Allows to boot Windows 7 in "Safe Mode" and shows the boot splash.
Signed-off-by: Patrick Rudolph siro@das-labor.org
diff --git a/vgasrc/cbvga.c b/vgasrc/cbvga.c index 39ffdbb..49af7f9 100644 --- a/vgasrc/cbvga.c +++ b/vgasrc/cbvga.c @@ -21,6 +21,10 @@ static u32 CBlinelength VAR16;
struct vgamode_s *cbvga_find_mode(int mode) { + /* Assume VGA compatible hardware in text-mode. */ + if (GET_GLOBAL(CBmode) == 0x3) + return stdvga_find_mode(mode); + if (mode == GET_GLOBAL(CBmode)) return &CBmodeinfo; if (mode == 0x03) @@ -31,11 +35,11 @@ struct vgamode_s *cbvga_find_mode(int mode) void cbvga_list_modes(u16 seg, u16 *dest, u16 *last) { - if (dest<last) { - SET_FARVAR(seg, *dest, GET_GLOBAL(CBmode)); - dest++; + + /* Assume VGA compatible hardware in text-mode. */ + if (GET_GLOBAL(CBmode) == 0x3) { + stdvga_list_modes(seg, dest, last); } - SET_FARVAR(seg, *dest, 0xffff); }
int @@ -98,6 +102,11 @@ int cbvga_set_mode(struct vgamode_s *vmode_g, int flags) { u8 emul = vmode_g == &CBemulinfo || GET_GLOBAL(CBmode) == 0x03; + + /* Assume VGA compatible hardware in text-mode. */ + if (GET_GLOBAL(CBmode) == 0x03) + return stdvga_set_mode(vmode_g, flags); + MASK_BDA_EXT(flags, BF_EMULATE_TEXT, emul ? BF_EMULATE_TEXT : 0); if (!(flags & MF_NOCLEARMEM)) { if (GET_GLOBAL(CBmodeinfo.memmodel) == MM_TEXT) {
Query the driver for the real scanline pitch in bytes.
As cbvga doesn't change the pitch on mode change, always return the same pitch, that might exceed width times Bytes-per-pixel.
Signed-off-by: Patrick Rudolph siro@das-labor.org
diff --git a/vgasrc/bochsvga.c b/vgasrc/bochsvga.c index ec5d101..458b3a8 100644 --- a/vgasrc/bochsvga.c +++ b/vgasrc/bochsvga.c @@ -310,6 +310,11 @@ bochsvga_save_restore(int cmd, u16 seg, void *data) return ret + (VBE_DISPI_INDEX_Y_OFFSET-VBE_DISPI_INDEX_XRES+1)*sizeof(u16); }
+int +bochsvga_get_linesize(struct vgamode_s *vmode_g) +{ + return DIV_ROUND_UP(vmode_g->width * vga_bpp(vmode_g), 8); +}
/**************************************************************** * Mode setting diff --git a/vgasrc/bochsvga.h b/vgasrc/bochsvga.h index ae5f75d..40ce6f1 100644 --- a/vgasrc/bochsvga.h +++ b/vgasrc/bochsvga.h @@ -52,6 +52,7 @@ int bochsvga_get_dacformat(struct vgamode_s *vmode_g); int bochsvga_set_dacformat(struct vgamode_s *vmode_g, int val); int bochsvga_save_restore(int cmd, u16 seg, void *data); int bochsvga_set_mode(struct vgamode_s *vmode_g, int flags); +int bochsvga_get_linesize(struct vgamode_s *vmode_g); int bochsvga_setup(void);
#endif // bochsvga.h diff --git a/vgasrc/cbvga.c b/vgasrc/cbvga.c index 49af7f9..6b3233e 100644 --- a/vgasrc/cbvga.c +++ b/vgasrc/cbvga.c @@ -124,6 +124,13 @@ cbvga_set_mode(struct vgamode_s *vmode_g, int flags) return 0; }
+int +cbvga_get_linesize(struct vgamode_s *vmode_g) +{ + /* Can't change mode, always report active pitch. */ + return GET_GLOBAL(CBlinelength); +} + #define CB_TAG_FRAMEBUFFER 0x0012 struct cb_framebuffer { u32 tag; diff --git a/vgasrc/clext.c b/vgasrc/clext.c index da8b790..08bc9b5 100644 --- a/vgasrc/clext.c +++ b/vgasrc/clext.c @@ -376,6 +376,11 @@ clext_save_restore(int cmd, u16 seg, void *data) return stdvga_save_restore(cmd, seg, data); }
+int +clext_get_linesize(struct vgamode_s *vmode_g) +{ + return DIV_ROUND_UP(vmode_g->width * vga_bpp(vmode_g), 8); +}
/**************************************************************** * Mode setting diff --git a/vgasrc/stdvga.c b/vgasrc/stdvga.c index 886deca..0e24297 100644 --- a/vgasrc/stdvga.c +++ b/vgasrc/stdvga.c @@ -321,6 +321,11 @@ stdvga_set_dacformat(struct vgamode_s *vmode_g, int val) return -1; }
+int +stdvga_get_linesize(struct vgamode_s *vmode_g) +{ + return DIV_ROUND_UP(vmode_g->width * vga_bpp(vmode_g), 8); +}
/**************************************************************** * Save/Restore state diff --git a/vgasrc/vbe.c b/vgasrc/vbe.c index facad19..724c1ba 100644 --- a/vgasrc/vbe.c +++ b/vgasrc/vbe.c @@ -107,7 +107,7 @@ vbe_104f01(struct bregs *regs) // Basic information about mode. int width = GET_GLOBAL(vmode_g->width); int height = GET_GLOBAL(vmode_g->height); - int linesize = DIV_ROUND_UP(width * vga_bpp(vmode_g), 8); + int linesize = vgahw_get_linesize(vmode_g); SET_FARVAR(seg, info->bytes_per_scanline, linesize); SET_FARVAR(seg, info->xres, width); SET_FARVAR(seg, info->yres, height); diff --git a/vgasrc/vgahw.h b/vgasrc/vgahw.h index dab2b4d..5839643 100644 --- a/vgasrc/vgahw.h +++ b/vgasrc/vgahw.h @@ -139,4 +139,14 @@ static inline int vgahw_save_restore(int cmd, u16 seg, void *data) { return stdvga_save_restore(cmd, seg, data); }
+static inline int vgahw_get_linesize(struct vgamode_s *vmode_g) { + if (CONFIG_VGA_CIRRUS) + return clext_get_linesize(vmode_g); + if (CONFIG_VGA_BOCHS) + return bochsvga_get_linesize(vmode_g); + if (CONFIG_VGA_COREBOOT) + return cbvga_get_linesize(vmode_g); + return stdvga_get_linesize(vmode_g); +} + #endif // vgahw.h diff --git a/vgasrc/vgautil.h b/vgasrc/vgautil.h index 08c4e8d..4b6f5b9 100644 --- a/vgasrc/vgautil.h +++ b/vgasrc/vgautil.h @@ -17,6 +17,7 @@ int cbvga_get_dacformat(struct vgamode_s *vmode_g); int cbvga_set_dacformat(struct vgamode_s *vmode_g, int val); int cbvga_save_restore(int cmd, u16 seg, void *data); int cbvga_set_mode(struct vgamode_s *vmode_g, int flags); +int cbvga_get_linesize(struct vgamode_s *vmode_g); int cbvga_setup(void);
// clext.c @@ -30,6 +31,7 @@ int clext_get_displaystart(struct vgamode_s *vmode_g); int clext_set_displaystart(struct vgamode_s *vmode_g, int val); int clext_save_restore(int cmd, u16 seg, void *data); int clext_set_mode(struct vgamode_s *vmode_g, int flags); +int clext_get_linesize(struct vgamode_s *vmode_g); struct bregs; void clext_1012(struct bregs *regs); int clext_setup(void); @@ -63,6 +65,7 @@ void stdvga_list_modes(u16 seg, u16 *dest, u16 *last); void stdvga_build_video_param(void); void stdvga_override_crtc(int mode, u8 *crtc); int stdvga_set_mode(struct vgamode_s *vmode_g, int flags); +int stdvga_get_linesize(struct vgamode_s *vmode_g); void stdvga_set_packed_palette(void);
// swcursor.c
As coreboot framebuffer is immutable always use CBmodeinfo.
Signed-off-by: Patrick Rudolph siro@das-labor.org
diff --git a/vgasrc/cbvga.c b/vgasrc/cbvga.c index 6b3233e..ae2bd4a 100644 --- a/vgasrc/cbvga.c +++ b/vgasrc/cbvga.c @@ -114,7 +114,7 @@ cbvga_set_mode(struct vgamode_s *vmode_g, int flags) return 0; } struct gfx_op op; - init_gfx_op(&op, vmode_g); + init_gfx_op(&op, &CBmodeinfo); op.x = op.y = 0; op.xlen = GET_GLOBAL(CBmodeinfo.width); op.ylen = GET_GLOBAL(CBmodeinfo.height);
Advertise compatible VESA modes, that are smaller or equal to coreboot's active framebuffer. Only modes that have the same Bpp are advertise and can be selected.
Allows the Windows 7 bootloader NTLDR to show up in VESA mode. Allows to show the Windows 7 boot logo. Allows Windows to boot in safe mode and in normal boot using VgaSave driver with resolution up to 1600x1200.
This fixes most likely other bootloader and operating systems as well, in case the are relying on VESA framebuffer support.
v2: Invalidate modes in cbvga_setup.
Signed-off-by: Patrick Rudolph siro@das-labor.org
diff --git a/vgasrc/cbvga.c b/vgasrc/cbvga.c index ae2bd4a..42a50f7 100644 --- a/vgasrc/cbvga.c +++ b/vgasrc/cbvga.c @@ -19,8 +19,73 @@ static struct vgamode_s CBmodeinfo VAR16; static struct vgamode_s CBemulinfo VAR16; static u32 CBlinelength VAR16;
+static struct cbvga_mode_s +{ + u16 mode; + struct vgamode_s info; +} cbvesa_modes[] VAR16 = { + /* VESA 1.0 modes */ + { 0x110, { MM_DIRECT, 640, 480, 15, 8, 16, SEG_GRAPH } }, + { 0x111, { MM_DIRECT, 640, 480, 16, 8, 16, SEG_GRAPH } }, + { 0x112, { MM_DIRECT, 640, 480, 24, 8, 16, SEG_GRAPH } }, + { 0x113, { MM_DIRECT, 800, 600, 15, 8, 16, SEG_GRAPH } }, + { 0x114, { MM_DIRECT, 800, 600, 16, 8, 16, SEG_GRAPH } }, + { 0x115, { MM_DIRECT, 800, 600, 24, 8, 16, SEG_GRAPH } }, + { 0x116, { MM_DIRECT, 1024, 768, 15, 8, 16, SEG_GRAPH } }, + { 0x117, { MM_DIRECT, 1024, 768, 16, 8, 16, SEG_GRAPH } }, + { 0x118, { MM_DIRECT, 1024, 768, 24, 8, 16, SEG_GRAPH } }, + { 0x119, { MM_DIRECT, 1280, 1024, 15, 8, 16, SEG_GRAPH } }, + { 0x11A, { MM_DIRECT, 1280, 1024, 16, 8, 16, SEG_GRAPH } }, + { 0x11B, { MM_DIRECT, 1280, 1024, 24, 8, 16, SEG_GRAPH } }, + { 0x11D, { MM_DIRECT, 1600, 1200, 15, 8, 16, SEG_GRAPH } }, + { 0x11E, { MM_DIRECT, 1600, 1200, 16, 8, 16, SEG_GRAPH } }, + { 0x11F, { MM_DIRECT, 1600, 1200, 24, 8, 16, SEG_GRAPH } }, + /* VESA 2.0 modes */ + { 0x141, { MM_DIRECT, 640, 400, 32, 8, 16, SEG_GRAPH } }, + { 0x142, { MM_DIRECT, 640, 480, 32, 8, 16, SEG_GRAPH } }, + { 0x143, { MM_DIRECT, 800, 600, 32, 8, 16, SEG_GRAPH } }, + { 0x144, { MM_DIRECT, 1024, 768, 32, 8, 16, SEG_GRAPH } }, + { 0x145, { MM_DIRECT, 1280, 1024, 32, 8, 16, SEG_GRAPH } }, + { 0x147, { MM_DIRECT, 1600, 1200, 32, 8, 16, SEG_GRAPH } }, + { 0x149, { MM_DIRECT, 1152, 864, 15, 8, 16, SEG_GRAPH } }, + { 0x14a, { MM_DIRECT, 1152, 864, 16, 8, 16, SEG_GRAPH } }, + { 0x14b, { MM_DIRECT, 1152, 864, 24, 8, 16, SEG_GRAPH } }, + { 0x14c, { MM_DIRECT, 1152, 864, 32, 8, 16, SEG_GRAPH } }, + { 0x175, { MM_DIRECT, 1280, 768, 16, 8, 16, SEG_GRAPH } }, + { 0x176, { MM_DIRECT, 1280, 768, 24, 8, 16, SEG_GRAPH } }, + { 0x177, { MM_DIRECT, 1280, 768, 32, 8, 16, SEG_GRAPH } }, + { 0x178, { MM_DIRECT, 1280, 800, 16, 8, 16, SEG_GRAPH } }, + { 0x179, { MM_DIRECT, 1280, 800, 24, 8, 16, SEG_GRAPH } }, + { 0x17a, { MM_DIRECT, 1280, 800, 32, 8, 16, SEG_GRAPH } }, + { 0x17b, { MM_DIRECT, 1280, 960, 16, 8, 16, SEG_GRAPH } }, + { 0x17c, { MM_DIRECT, 1280, 960, 24, 8, 16, SEG_GRAPH } }, + { 0x17d, { MM_DIRECT, 1280, 960, 32, 8, 16, SEG_GRAPH } }, + { 0x17e, { MM_DIRECT, 1440, 900, 16, 8, 16, SEG_GRAPH } }, + { 0x17f, { MM_DIRECT, 1440, 900, 24, 8, 16, SEG_GRAPH } }, + { 0x180, { MM_DIRECT, 1440, 900, 32, 8, 16, SEG_GRAPH } }, + { 0x181, { MM_DIRECT, 1400, 1050, 16, 8, 16, SEG_GRAPH } }, + { 0x182, { MM_DIRECT, 1400, 1050, 24, 8, 16, SEG_GRAPH } }, + { 0x183, { MM_DIRECT, 1400, 1050, 32, 8, 16, SEG_GRAPH } }, + { 0x184, { MM_DIRECT, 1680, 1050, 16, 8, 16, SEG_GRAPH } }, + { 0x185, { MM_DIRECT, 1680, 1050, 24, 8, 16, SEG_GRAPH } }, + { 0x186, { MM_DIRECT, 1680, 1050, 32, 8, 16, SEG_GRAPH } }, + { 0x187, { MM_DIRECT, 1920, 1200, 16, 8, 16, SEG_GRAPH } }, + { 0x188, { MM_DIRECT, 1920, 1200, 24, 8, 16, SEG_GRAPH } }, + { 0x189, { MM_DIRECT, 1920, 1200, 32, 8, 16, SEG_GRAPH } }, + { 0x18a, { MM_DIRECT, 2560, 1600, 16, 8, 16, SEG_GRAPH } }, + { 0x18b, { MM_DIRECT, 2560, 1600, 24, 8, 16, SEG_GRAPH } }, + { 0x18c, { MM_DIRECT, 2560, 1600, 32, 8, 16, SEG_GRAPH } }, + { 0x18d, { MM_DIRECT, 1280, 720, 16, 8, 16, SEG_GRAPH } }, + { 0x18e, { MM_DIRECT, 1280, 720, 24, 8, 16, SEG_GRAPH } }, + { 0x18f, { MM_DIRECT, 1280, 720, 32, 8, 16, SEG_GRAPH } }, + { 0x190, { MM_DIRECT, 1920, 1080, 16, 8, 16, SEG_GRAPH } }, + { 0x191, { MM_DIRECT, 1920, 1080, 24, 8, 16, SEG_GRAPH } }, + { 0x192, { MM_DIRECT, 1920, 1080, 32, 8, 16, SEG_GRAPH } }, +}; + struct vgamode_s *cbvga_find_mode(int mode) { + int i; /* Assume VGA compatible hardware in text-mode. */ if (GET_GLOBAL(CBmode) == 0x3) return stdvga_find_mode(mode); @@ -29,16 +94,45 @@ struct vgamode_s *cbvga_find_mode(int mode) return &CBmodeinfo; if (mode == 0x03) return &CBemulinfo; + + for (i = 0; i < ARRAY_SIZE(cbvesa_modes); i++) { + struct cbvga_mode_s *cbmode_g = &cbvesa_modes[i]; + if (GET_GLOBAL(cbmode_g->mode) == 0xffff) + continue; + if (GET_GLOBAL(cbmode_g->mode) == mode) + return &cbmode_g->info; + } return NULL; }
void cbvga_list_modes(u16 seg, u16 *dest, u16 *last) { + int i;
/* Assume VGA compatible hardware in text-mode. */ if (GET_GLOBAL(CBmode) == 0x3) { stdvga_list_modes(seg, dest, last); + } else { + /* Advertise additional SVGA modes for Microsoft NTLDR graphical mode. + * Microsoft NTLDR: + * + Graphical mode uses a maximum resolution of 1600x1200. + * + Expects to find VESA mode with 800x600 or 1024x768. + * + 24 Bpp and 32 Bpp are supported + */ + for (i = 0; i < ARRAY_SIZE(cbvesa_modes) && dest < last; i++) { + struct cbvga_mode_s *cbmode_g = &cbvesa_modes[i]; + u16 mode = GET_GLOBAL(cbmode_g->mode); + if (mode == 0xffff) + continue; + SET_FARVAR(seg, *dest, mode); + dest++; + } + if (dest < last) { + SET_FARVAR(seg, *dest, GET_GLOBAL(CBmode)); + dest++; + } + SET_FARVAR(seg, *dest, 0xffff); } }
@@ -154,6 +248,7 @@ struct cb_framebuffer { int cbvga_setup(void) { + int i; dprintf(1, "coreboot vga init\n");
if (GET_GLOBAL(HaveRunInit)) @@ -208,5 +303,16 @@ cbvga_setup(void) memcpy_far(get_global_seg(), &CBemulinfo , get_global_seg(), &CBmodeinfo, sizeof(CBemulinfo));
+ // Validate modes + for (i = 0; i < ARRAY_SIZE(cbvesa_modes); i++) { + struct cbvga_mode_s *cbmode_g = &cbvesa_modes[i]; + /* Skip VBE modes that doesn't fit into coreboot's framebuffer */ + if ((GET_GLOBAL(cbmode_g->info.height) > ylines) + || (GET_GLOBAL(cbmode_g->info.width) > xlines) + || (GET_GLOBAL(cbmode_g->info.depth) != bpp)) { + dprintf(3, "Removing mode %x\n", GET_GLOBAL(cbmode_g->mode)); + SET_VGA(cbmode_g->mode, 0xffff); + } + } return 0; }
Required for Intel VBT to copy memory.
Signed-off-by: Patrick Rudolph siro@das-labor.org
diff --git a/vgasrc/vgafb.c b/vgasrc/vgafb.c index 46adfb5..ef0c4c2 100644 --- a/vgasrc/vgafb.c +++ b/vgasrc/vgafb.c @@ -187,7 +187,7 @@ gfx_packed(struct gfx_op *op) ****************************************************************/
// Use int 1587 call to copy memory to/from the framebuffer. -static void +void memcpy_high(void *dest, void *src, u32 len) { u64 gdt[6]; diff --git a/vgasrc/vgafb.h b/vgasrc/vgafb.h index ccdc703..dde52e0 100644 --- a/vgasrc/vgafb.h +++ b/vgasrc/vgafb.h @@ -38,5 +38,5 @@ void vgafb_write_char(struct cursorpos cp, struct carattr ca); struct carattr vgafb_read_char(struct cursorpos cp); void vgafb_write_pixel(u8 color, u16 x, u16 y); u8 vgafb_read_pixel(u16 x, u16 y); - +void memcpy_high(void *dest, void *src, u32 len); #endif // vgafb.h
Add support for providing Intel VBT OpRegion.
Intel's legacy VGA driver expect the VBT (Video BIOS Table) to be present in the VGA option ROM. As of now the VBT is not present and you will (not) see a BSOD as the legacy driver doesn't check the ASLS register, as newer versions of the driver do.
Parse Intel's OpRegion and copy the VBT to the end of VGA option rom, if present, to prevent a BSOD. In case the ASLS register is not set or the OpRegion is invalid no VBT will be installed.
Tested on GM45 (Lenovo T500) using the VBT extracted from Intel VBIOS, as the faked VBT, generated by coreboot, doesn't work.
Signed-off-by: Patrick Rudolph siro@das-labor.org
diff --git a/vgasrc/cbvga.c b/vgasrc/cbvga.c index 42a50f7..bc5b79e 100644 --- a/vgasrc/cbvga.c +++ b/vgasrc/cbvga.c @@ -13,6 +13,9 @@ #include "vgabios.h" // SET_VGA #include "vgafb.h" // handle_gfx_op #include "vgautil.h" // VBE_total_memory +#include "hw/pci.h" // pci_config_readw +#include "hw/pci_regs.h" // PCI_VENDOR_ID +#include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL
static int CBmode VAR16; static struct vgamode_s CBmodeinfo VAR16; @@ -225,6 +228,117 @@ cbvga_get_linesize(struct vgamode_s *vmode_g) return GET_GLOBAL(CBlinelength); }
+struct optionrom_vbt { + u32 hdr_signature; + u8 reserved[16]; + u16 hdr_version; + u16 hdr_size; + u16 hdr_vbt_size; +}; + +struct opregion_header { + u8 signature[16]; + u32 size; + u32 version; +}; + +static const u8 igd_opregion_signature[] VAR16 = { + 'I', 'n', 't', 'e', 'l', 'G', 'r', 'a', 'p', 'h','i','c','s','M','e','m' +}; + +#define VBT_SIGNATURE 0x54425624 +#define ASLS 0xfc +#define IGD_OPREGION_VBT_OFFSET 0x400 + +/* Compares to memory regions */ +static int +cbvga_memcpm(u8 *src1, u8 *src2, u8 len) +{ + while (len --) { + if (GET_FARVAR(0, *src1) != GET_GLOBAL(*src2)) + return 1; + src1 ++; + src2 ++; + } + + return 0; +} + +/* Fetch and install Intel VBT OpRegion */ +static void +cbvga_setup_intel_vbt(void) +{ + struct optionrom_vbt *vbt; + struct opregion_header *oph; + u16 offset; + u8 size; + + /* Verify OpRegion Base address */ + if ((pci_config_readl(pci_to_bdf(0, 2, 0), ASLS) == 0x00000000) + || (pci_config_readl(pci_to_bdf(0, 2, 0), ASLS) == 0xffffffff)) + return; + + dprintf(3, "found valid ASLS.\n"); + + /* Get OpRegion Base address */ + oph = (void *)pci_config_readl(pci_to_bdf(0, 2, 0), ASLS); + + /* Verify OpRegion */ + if (cbvga_memcpm(oph->signature, (u8 *)igd_opregion_signature + , sizeof(oph->signature)) != 0) + return; + + dprintf(3, "found valid Intel OpRegion version %d.\n" + , GET_FARVAR(0, oph->version)); + + /* Get VBT */ + vbt = ((void *)oph) + IGD_OPREGION_VBT_OFFSET; + + /* Verify VBT */ + if ((GET_FARVAR(0, vbt->hdr_signature) != VBT_SIGNATURE) + || (GET_FARVAR(0, vbt->hdr_vbt_size) == 0)) + return; + + dprintf(3, "Found valid VBT with size %d.\n", GET_FARVAR(0, vbt->hdr_vbt_size)); + + /* Verify Option Rom free space */ + extern u8 _rom_header_size; + size = GET_GLOBAL(_rom_header_size); + offset = size * 512; + if ((GET_FARVAR(0, vbt->hdr_vbt_size) + offset) > 0xffff) + return; + + dprintf(3, "Enough space in rom to place VBT.\n"); + + /* Copy VBT */ + memcpy_high((void *)(offset + BUILD_ROM_START), vbt + , GET_FARVAR(0, vbt->hdr_vbt_size)); + + dprintf(3, "Copied VBT.\n"); + + /* Increment rom header size */ + size += (GET_FARVAR(0, vbt->hdr_vbt_size) + 512 - 1) >> 9; + SET_VGA(_rom_header_size, size); + + dprintf(3, "Incremented rom size from %d to %d bytes.\n" + , offset, size * 512); + + /* Set VBT offset */ + extern u16 _rom_header_pnpdata; + SET_VGA(_rom_header_pnpdata, offset); + + dprintf(3, "Wrote VBT pointer.\n"); + + /* Verify */ + vbt = (void *)(offset + BUILD_ROM_START); + if ((GET_FARVAR(0, vbt->hdr_signature) != VBT_SIGNATURE) + || (GET_FARVAR(0, vbt->hdr_vbt_size) == 0)) + return; + + dprintf(1, "Successfully installed Intel VBT.\n"); +} + + #define CB_TAG_FRAMEBUFFER 0x0012 struct cb_framebuffer { u32 tag; @@ -254,6 +368,13 @@ cbvga_setup(void) if (GET_GLOBAL(HaveRunInit)) return 0;
+ if (pci_config_readw(pci_to_bdf(0, 2, 0), PCI_VENDOR_ID) + == PCI_VENDOR_ID_INTEL) { + dprintf(1, "Found Intel GPU, trying to install VBT.\n"); + + cbvga_setup_intel_vbt(); + } + struct cb_header *cbh = find_cb_table(); if (!cbh) { dprintf(1, "Unable to find coreboot table\n"); diff --git a/vgasrc/vgaentry.S b/vgasrc/vgaentry.S index 53be2b3..10b1b80 100644 --- a/vgasrc/vgaentry.S +++ b/vgasrc/vgaentry.S @@ -16,7 +16,7 @@
.section .rom.header .code16 - .global _rom_header, _rom_header_size, _rom_header_checksum + .global _rom_header, _rom_header_size, _rom_header_checksum, _rom_header_pnpdata _rom_header: .word 0xaa55 _rom_header_size:
Add a crash detection handler to detect a crash on mode change and don't clear the screen on mode change ever again.
This is a workaround for Microsoft Windows doing some magic to the INT 15 handler, preventing memcpy_high to function properly.
Allows Microsoft's Windows 7 to boot in safe mode. Some artifacts might stay in the unused part of the framebuffer, but at least it is possible to use all advertised VESA modes.
Signed-off-by: Patrick Rudolph siro@das-labor.org
diff --git a/vgasrc/cbvga.c b/vgasrc/cbvga.c index bc5b79e..af4b138 100644 --- a/vgasrc/cbvga.c +++ b/vgasrc/cbvga.c @@ -195,6 +195,8 @@ cbvga_save_restore(int cmd, u16 seg, void *data) return bda_save_restore(cmd, seg, data); }
+static u16 crash_detect VAR16; + int cbvga_set_mode(struct vgamode_s *vmode_g, int flags) { @@ -210,6 +212,18 @@ cbvga_set_mode(struct vgamode_s *vmode_g, int flags) memset16_far(SEG_CTEXT, (void*)0, 0x0720, 80*25*2); return 0; } + /* Windows replaced INT 15h handler, resulting in a crash, as + * mempcpy_high doesn't work any more. + * We can't do anything about that, but try to detect the crash + * and don't clear the screen. + */ + if (GET_GLOBAL(crash_detect) == 0xdead) { + dprintf(1, "Detected crash, skipping clear screen.\n"); + return 0; + } + /* Install crash signal */ + SET_VGA(crash_detect, 0xdead); + struct gfx_op op; init_gfx_op(&op, &CBmodeinfo); op.x = op.y = 0; @@ -217,6 +231,9 @@ cbvga_set_mode(struct vgamode_s *vmode_g, int flags) op.ylen = GET_GLOBAL(CBmodeinfo.height); op.op = GO_MEMSET; handle_gfx_op(&op); + + /* Reset crash signal */ + SET_VGA(crash_detect, 0); } return 0; }
On Fri, Mar 24, 2017 at 05:27:19PM +0100, Patrick Rudolph wrote:
This series add several fixes to coreboot's framebuffer implementation and it main purpose is to fix Microsoft's Windows as supported OS.
The series fixes the following issues:
- Windows shows no image in text-mode
- Windows NTLDR/bootmgr shows no image in VESA mode
- Windows shows no image in VESA mode
- Windows legacy Intel VGA driver requires VBT in VGA option ROM
- Clear screen broken due to INT 15h replaced by OS
I was able to boot Windows with the following configurations:
- using VgaSave driver in text-mode with a fixed display resolution of 640x480 at 4Bpp.
- using VgaSave driver in VESA mode and set a display resolution up to 1600x1200 at 24Bpp.
- using VgaSave driver in Windows's "Safe Mode" in VESA mode and set a display resolution up to 1600x1200 at 24Bpp.
- using the Intel VGA driver with up to the panel native resolution of 1680x1050 at 24Bpp.
Most likely other VESA compatible bootloaders and operating systems will be fixed, too.
Changes since v1:
Thanks, but I didn't see a response to my comments on patches 1 and 2 from the last series.
- Add crash detection handler for INT 15h modifications.
Can you explain how this works? If int1587 is disabled, why is it a priority to not clear the screen?
- Copy Intel VBT to VGA option ROM if provided by ASLS
As a high level comment, I think it would be great to have further support for Intel video in SeaVGABIOS, but I wonder if extending cbvga is the right approach. Seems like we could make an intel-seavgabios and it would be a better platfrom for future intel specific features (like auto-detecting the framebuffer and maybe even switching modes).
Cheers, -Kevin