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: