summaryrefslogtreecommitdiff
path: root/src/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers')
-rw-r--r--src/drivers/intel/gma/opregion.c206
1 files changed, 164 insertions, 42 deletions
diff --git a/src/drivers/intel/gma/opregion.c b/src/drivers/intel/gma/opregion.c
index e9cbba13cc..84bca9bd3a 100644
--- a/src/drivers/intel/gma/opregion.c
+++ b/src/drivers/intel/gma/opregion.c
@@ -17,6 +17,7 @@
#include <arch/acpi.h>
#include <types.h>
#include <string.h>
+#include <cbfs.h>
#include <device/device.h>
#include <device/pci.h>
#include <device/pci_ids.h>
@@ -67,70 +68,195 @@ void intel_gma_restore_opregion(void)
}
}
-static void *get_intel_vbios(void)
+static enum cb_err vbt_validate(struct region_device *rdev)
{
- /* This should probably be looking at CBFS or we should always
- * deploy the VBIOS on Intel systems, even if we don't run it
- * in coreboot (e.g. SeaBIOS only scenarios).
- */
- u8 *vbios = (u8 *)0xc0000;
+ uint32_t sig;
+
+ if (rdev_readat(rdev, &sig, 0, sizeof(sig)) != sizeof(sig))
+ return CB_ERR;
+
+ if (sig != VBT_SIGNATURE)
+ return CB_ERR;
+
+ return CB_SUCCESS;
+}
+
+static enum cb_err locate_vbt_vbios(const u8 *vbios, struct region_device *rdev)
+{
+ const optionrom_header_t *oprom;
+ const optionrom_pcir_t *pcir;
+ struct region_device rd;
+ enum cb_err ret;
+ u8 opromsize;
+ size_t offset;
+
+ // FIXME: caller should supply a region_device instead of vbios pointer
+ if (rdev_chain(&rd, &addrspace_32bit.rdev, (uintptr_t)vbios,
+ sizeof(*oprom)))
+ return CB_ERR;
+
+ if (rdev_readat(&rd, &opromsize, offsetof(optionrom_header_t, size),
+ sizeof(opromsize)) != sizeof(opromsize) || !opromsize)
+ return CB_ERR;
+
+ if (rdev_chain(&rd, &addrspace_32bit.rdev, (uintptr_t)vbios,
+ opromsize * 512))
+ return CB_ERR;
+
+ oprom = rdev_mmap(&rd, 0, sizeof(*oprom));
+ if (!oprom)
+ return CB_ERR;
+
+ if (!oprom->pcir_offset || !oprom->vbt_offset) {
+ rdev_munmap(&rd, (void *)oprom);
+ return CB_ERR;
+ }
- optionrom_header_t *oprom = (optionrom_header_t *)vbios;
- optionrom_pcir_t *pcir = (optionrom_pcir_t *)(vbios +
- oprom->pcir_offset);
+ pcir = rdev_mmap(&rd, oprom->pcir_offset, sizeof(*pcir));
+ if (pcir == NULL) {
+ rdev_munmap(&rd, (void *)oprom);
+ return CB_ERR;
+ }
- printk(BIOS_DEBUG, "GET_VBIOS: %x %x %x %x %x\n",
+ printk(BIOS_DEBUG, "GMA: locate_vbt_vbios: %x %x %x %x %x\n",
oprom->signature, pcir->vendor, pcir->classcode[0],
pcir->classcode[1], pcir->classcode[2]);
+ /* Make sure we got an Intel VGA option rom */
+ if ((oprom->signature != OPROM_SIGNATURE) ||
+ (pcir->vendor != PCI_VENDOR_ID_INTEL) ||
+ (pcir->signature != 0x52494350) ||
+ (pcir->classcode[0] != 0x00) ||
+ (pcir->classcode[1] != 0x00) ||
+ (pcir->classcode[2] != 0x03)) {
+ rdev_munmap(&rd, (void *)oprom);
+ rdev_munmap(&rd, (void *)pcir);
+ return CB_ERR;
+ }
+
+ rdev_munmap(&rd, (void *)pcir);
+
+ /* Search for $VBT as some VBIOS are broken... */
+ offset = oprom->vbt_offset;
+ do {
+ ret = rdev_chain(rdev, &rd, offset,
+ (opromsize * 512) - offset);
+ offset++;
+ } while (ret == CB_SUCCESS && vbt_validate(rdev) != CB_SUCCESS);
- if ((oprom->signature == OPROM_SIGNATURE) &&
- (pcir->vendor == PCI_VENDOR_ID_INTEL) &&
- (pcir->classcode[0] == 0x00) &&
- (pcir->classcode[1] == 0x00) &&
- (pcir->classcode[2] == 0x03))
- return (void *)vbios;
+ offset--;
- return NULL;
+ if (ret == CB_SUCCESS && offset != oprom->vbt_offset)
+ printk(BIOS_WARNING, "GMA: Buggy VBIOS found\n");
+ else if (ret != CB_SUCCESS)
+ printk(BIOS_ERR, "GMA: Broken VBIOS found\n");
+
+ rdev_munmap(&rd, (void *)oprom);
+ return ret;
}
-static enum cb_err init_opregion_vbt(igd_opregion_t *opregion)
+static enum cb_err locate_vbt_cbfs(struct region_device *rdev)
{
- void *vbios;
- vbios = get_intel_vbios();
- if (!vbios) {
- printk(BIOS_DEBUG, "VBIOS not found.\n");
- return CB_ERR;
+ struct cbfsf file_desc;
+
+ /* try to locate vbt.bin in CBFS */
+ if (cbfs_boot_locate(&file_desc, "vbt.bin", NULL) == CB_SUCCESS) {
+ cbfs_file_data(rdev, &file_desc);
+ printk(BIOS_INFO, "GMA: Found VBT in CBFS\n");
+ return CB_SUCCESS;
}
- printk(BIOS_DEBUG, " ... VBIOS found at %p\n", vbios);
- optionrom_header_t *oprom = (optionrom_header_t *)vbios;
- optionrom_vbt_t *vbt = (optionrom_vbt_t *)(vbios +
- oprom->vbt_offset);
+ return CB_ERR;
+}
- if (read32(vbt->hdr_signature) != VBT_SIGNATURE) {
- printk(BIOS_DEBUG, "VBT not found!\n");
+static enum cb_err locate_vbt_vbios_cbfs(struct region_device *rdev)
+{
+ const u8 *oprom =
+ (const u8 *)pci_rom_probe(dev_find_slot(0, PCI_DEVFN(0x2, 0)));
+ if (oprom == NULL)
return CB_ERR;
- }
- memcpy(opregion->header.vbios_version, vbt->coreblock_biosbuild, 4);
- memcpy(opregion->vbt.gvd1, vbt, vbt->hdr_vbt_size < 7168 ?
- vbt->hdr_vbt_size : 7168);
+ printk(BIOS_INFO, "GMA: Found VBIOS in CBFS\n");
- return CB_SUCCESS;
+ return locate_vbt_vbios(oprom, rdev);
}
/* Initialize IGD OpRegion, called from ACPI code and OS drivers */
-enum cb_err intel_gma_init_igd_opregion(igd_opregion_t *opregion)
+enum cb_err
+intel_gma_init_igd_opregion(igd_opregion_t *opregion)
{
- enum cb_err ret;
+ struct region_device rdev;
+ optionrom_vbt_t *vbt = NULL;
+ optionrom_vbt_t *ext_vbt;
+ bool found = false;
- memset((void *)opregion, 0, sizeof(igd_opregion_t));
+ /* Search for vbt.bin in CBFS. */
+ if (locate_vbt_cbfs(&rdev) == CB_SUCCESS &&
+ vbt_validate(&rdev) == CB_SUCCESS) {
+ found = true;
+ printk(BIOS_INFO, "GMA: Found valid VBT in CBFS\n");
+ }
+ /* Search for pci8086,XXXX.rom in CBFS. */
+ else if (locate_vbt_vbios_cbfs(&rdev) == CB_SUCCESS &&
+ vbt_validate(&rdev) == CB_SUCCESS) {
+ found = true;
+ printk(BIOS_INFO, "GMA: Found valid VBT in VBIOS\n");
+ }
+ /*
+ * Try to locate Intel VBIOS at 0xc0000. It might have been placed by
+ * Native Graphics Init as fake Option ROM or when coreboot did run the
+ * VBIOS on legacy platforms.
+ * TODO: Place generated fake VBT in CBMEM and get rid of this.
+ */
+ else if (locate_vbt_vbios((u8 *)0xc0000, &rdev) == CB_SUCCESS &&
+ vbt_validate(&rdev) == CB_SUCCESS) {
+ found = true;
+ printk(BIOS_INFO, "GMA: Found valid VBT in legacy area\n");
+ }
- // FIXME if IGD is disabled, we should exit here.
+ if (!found) {
+ printk(BIOS_ERR, "GMA: VBT couldn't be found\n");
+ return CB_ERR;
+ }
+
+ vbt = rdev_mmap_full(&rdev);
+ if (!vbt) {
+ printk(BIOS_ERR, "GMA: Error mapping VBT\n");
+ return CB_ERR;
+ }
+
+ if (vbt->hdr_vbt_size > region_device_sz(&rdev)) {
+ printk(BIOS_ERR, "GMA: Error mapped only a partial VBT\n");
+ rdev_munmap(&rdev, vbt);
+ return CB_ERR;
+ }
+
+ memset(opregion, 0, sizeof(igd_opregion_t));
memcpy(&opregion->header.signature, IGD_OPREGION_SIGNATURE,
sizeof(opregion->header.signature));
+ memcpy(opregion->header.vbios_version, vbt->coreblock_biosbuild,
+ ARRAY_SIZE(vbt->coreblock_biosbuild));
+ /* Extended VBT support */
+ if (vbt->hdr_vbt_size > sizeof(opregion->vbt.gvd1)) {
+ ext_vbt = cbmem_add(CBMEM_ID_EXT_VBT, vbt->hdr_vbt_size);
+
+ if (ext_vbt == NULL) {
+ printk(BIOS_ERR,
+ "GMA: Unable to add Ext VBT to cbmem!\n");
+ rdev_munmap(&rdev, vbt);
+ return CB_ERR;
+ }
+
+ memcpy(ext_vbt, vbt, vbt->hdr_vbt_size);
+ opregion->mailbox3.rvda = (uintptr_t)ext_vbt;
+ opregion->mailbox3.rvds = vbt->hdr_vbt_size;
+ } else {
+ /* Raw VBT size which can fit in gvd1 */
+ memcpy(opregion->vbt.gvd1, vbt, vbt->hdr_vbt_size);
+ }
+
+ rdev_munmap(&rdev, vbt);
/* 8kb */
opregion->header.size = sizeof(igd_opregion_t) / 1024;
@@ -158,10 +284,6 @@ enum cb_err intel_gma_init_igd_opregion(igd_opregion_t *opregion)
opregion->mailbox3.bclm[9] = IGD_WORD_FIELD_VALID + 0x5ae5;
opregion->mailbox3.bclm[10] = IGD_WORD_FIELD_VALID + 0x64ff;
- ret = init_opregion_vbt(opregion);
- if (ret != CB_SUCCESS)
- return ret;
-
/* Write ASLS PCI register and prepare SWSCI register. */
intel_gma_opregion_register((uintptr_t)opregion);