[U-Boot] [PATCH 07/19] pci: Add general support for execution of video ROMs

Simon Glass sjg at chromium.org
Sat Nov 15 04:56:33 CET 2014


Some platforms don't have native code for dealing with their video
hardware. In some cases they use a binary blob to set it up and perform
required actions like setting the video mode. This approach is a hangover
from the old PC days where a ROM was provided and executed during startup.

Even now, these ROMs are supplied as a way to set up video. It avoids the
code for every video chip needing to be provided in the boot loader. But
it makes the video much less flexible - e.g. it is not possible to do
anything else while the video init is happening (including waiting hundreds
of milliseconds for display panels to start up).

In any case, to deal with this sad state of affairs, provide an API for
execution of x86 video ROMs, either natively or through emulation.

Signed-off-by: Simon Glass <sjg at chromium.org>
---

 drivers/pci/Makefile  |   2 +-
 drivers/pci/pci_rom.c | 276 ++++++++++++++++++++++++++++++++++++++++++++++++++
 include/bios_emul.h   |  14 +++
 include/pci_rom.h     |  58 +++++++++++
 4 files changed, 349 insertions(+), 1 deletion(-)
 create mode 100644 drivers/pci/pci_rom.c
 create mode 100644 include/pci_rom.h

diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index e73a498..55d6a9b 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -6,7 +6,7 @@
 #
 
 obj-$(CONFIG_FSL_PCI_INIT) += fsl_pci_init.o
-obj-$(CONFIG_PCI) += pci.o pci_auto.o
+obj-$(CONFIG_PCI) += pci.o pci_auto.o pci_rom.o
 obj-$(CONFIG_PCI_INDIRECT_BRIDGE) += pci_indirect.o
 obj-$(CONFIG_PCI_GT64120) += pci_gt64120.o
 obj-$(CONFIG_PCI_MSC01) += pci_msc01.o
diff --git a/drivers/pci/pci_rom.c b/drivers/pci/pci_rom.c
new file mode 100644
index 0000000..c47ffa6
--- /dev/null
+++ b/drivers/pci/pci_rom.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2014 Google, Inc
+ *
+ * From coreboot, originally based on the Linux kernel (drivers/pci/pci.c).
+ *
+ * Modifications are:
+ * Copyright (C) 2003-2004 Linux Networx
+ * (Written by Eric Biederman <ebiederman at lnxi.com> for Linux Networx)
+ * Copyright (C) 2003-2006 Ronald G. Minnich <rminnich at gmail.com>
+ * Copyright (C) 2004-2005 Li-Ta Lo <ollie at lanl.gov>
+ * Copyright (C) 2005-2006 Tyan
+ * (Written by Yinghai Lu <yhlu at tyan.com> for Tyan)
+ * Copyright (C) 2005-2009 coresystems GmbH
+ * (Written by Stefan Reinauer <stepan at coresystems.de> for coresystems GmbH)
+ *
+ * PCI Bus Services, see include/linux/pci.h for further explanation.
+ *
+ * Copyright 1993 -- 1997 Drew Eckhardt, Frederic Potter,
+ * David Mosberger-Tang
+ *
+ * Copyright 1997 -- 1999 Martin Mares <mj at atrey.karlin.mff.cuni.cz>
+
+ * SPDX-License-Identifier:	GPL-2.0
+ */
+
+#include <common.h>
+#include <bios_emul.h>
+#include <errno.h>
+#include <malloc.h>
+#include <pci.h>
+#include <pci_rom.h>
+#include <vbe.h>
+#include <video_fb.h>
+
+#ifdef CONFIG_HAVE_ACPI_RESUME
+#include <asm/acpi.h>
+#endif
+
+__weak bool board_should_run_oprom(pci_dev_t dev)
+{
+	return true;
+}
+
+static bool should_load_oprom(pci_dev_t dev)
+{
+#ifdef CONFIG_HAVE_ACPI_RESUME
+	if (acpi_get_slp_type() == 3)
+		return false;
+#endif
+	if (IS_ENABLED(CONFIG_ALWAYS_LOAD_OPROM))
+		return 1;
+	if (board_should_run_oprom(dev))
+		return 1;
+
+	return 0;
+}
+
+__weak uint32_t board_map_oprom_vendev(uint32_t vendev)
+{
+	return vendev;
+}
+
+static int pci_rom_probe(pci_dev_t dev, uint class,
+			 struct pci_rom_header **hdrp)
+{
+	struct pci_rom_header *rom_header;
+	struct pci_rom_data *rom_data;
+	u16 vendor, device;
+	u32 vendev;
+	u32 mapped_vendev;
+	u32 rom_address;
+
+	pci_read_config_word(dev, PCI_VENDOR_ID, &vendor);
+	pci_read_config_word(dev, PCI_DEVICE_ID, &device);
+	vendev = vendor << 16 | device;
+	mapped_vendev = board_map_oprom_vendev(vendev);
+	if (vendev != mapped_vendev)
+		debug("Device ID mapped to %#08x\n", mapped_vendev);
+
+#ifdef CONFIG_X86_OPTION_ROM_ADDR
+	rom_address = CONFIG_X86_OPTION_ROM_ADDR;
+#else
+	pci_write_config_dword(dev, PCI_ROM_ADDRESS, (u32)PCI_ROM_ADDRESS_MASK);
+	pci_read_config_dword(dev, PCI_ROM_ADDRESS, &rom_address);
+	if (rom_address == 0x00000000 || rom_address == 0xffffffff) {
+		debug("%s: rom_address=%x\n", __func__, rom_address);
+		return -ENOENT;
+	}
+
+	/* Enable expansion ROM address decoding. */
+	pci_write_config_dword(dev, PCI_ROM_ADDRESS,
+			       rom_address | PCI_ROM_ADDRESS_ENABLE);
+#endif
+	debug("Option ROM address %x\n", rom_address);
+	rom_header = (struct pci_rom_header *)rom_address;
+
+	debug("PCI expansion ROM, signature %#04x, INIT size %#04x, data ptr %#04x\n",
+	      le32_to_cpu(rom_header->signature),
+	      rom_header->size * 512, le32_to_cpu(rom_header->data));
+
+	if (le32_to_cpu(rom_header->signature) != PCI_ROM_HDR) {
+		printf("Incorrect expansion ROM header signature %04x\n",
+		       le32_to_cpu(rom_header->signature));
+		return -EINVAL;
+	}
+
+	rom_data = (((void *)rom_header) + le32_to_cpu(rom_header->data));
+
+	debug("PCI ROM image, vendor ID %04x, device ID %04x,\n",
+	      rom_data->vendor, rom_data->device);
+
+	/* If the device id is mapped, a mismatch is expected */
+	if ((vendor != rom_data->vendor || device != rom_data->device) &&
+	    (vendev == mapped_vendev)) {
+		printf("ID mismatch: vendor ID %04x, device ID %04x\n",
+		       rom_data->vendor, rom_data->device);
+		return -EPERM;
+	}
+
+	debug("PCI ROM image, Class Code %04x%02x, Code Type %02x\n",
+	      rom_data->class_hi, rom_data->class_lo, rom_data->type);
+
+	if (class != ((rom_data->class_hi << 8) | rom_data->class_lo)) {
+		debug("Class Code mismatch ROM %08x, dev %08x\n",
+		      (rom_data->class_hi << 8) | rom_data->class_lo,
+		      class);
+	}
+	*hdrp = rom_header;
+
+	return 0;
+}
+
+int pci_rom_load(uint16_t class, struct pci_rom_header *rom_header,
+		 struct pci_rom_header **ram_headerp)
+{
+	struct pci_rom_data *rom_data;
+	unsigned int rom_size;
+	unsigned int image_size = 0;
+	void *target;
+
+	do {
+		/* Get next image, until we see an x86 version */
+		rom_header = (struct pci_rom_header *)((void *)rom_header +
+							    image_size);
+
+		rom_data = (struct pci_rom_data *)((void *)rom_header +
+				le32_to_cpu(rom_header->data));
+
+		image_size = le32_to_cpu(rom_data->ilen) * 512;
+	} while ((rom_data->type != 0) && (rom_data->indicator != 0));
+
+	if (rom_data->type != 0)
+		return -EACCES;
+
+	rom_size = rom_header->size * 512;
+
+	target = (void *)PCI_VGA_RAM_IMAGE_START;
+	if (target != rom_header) {
+		debug("Copying VGA ROM Image from %p to %p, 0x%x bytes\n",
+		      rom_header, target, rom_size);
+		memcpy(target, rom_header, rom_size);
+		if (memcmp(target, rom_header, rom_size)) {
+			printf("VGA ROM copy failed\n");
+			return -EFAULT;
+		}
+	}
+	*ram_headerp = target;
+
+	return 0;
+}
+
+static struct vbe_mode_info mode_info;
+
+int vbe_get_video_info(struct graphic_device *gdev)
+{
+#ifdef CONFIG_FRAMEBUFFER_SET_VESA_MODE
+	struct vesa_mode_info *vesa = &mode_info.vesa;
+
+	gdev->winSizeX = vesa->x_resolution;
+	gdev->winSizeY = vesa->y_resolution;
+
+	gdev->plnSizeX = vesa->x_resolution;
+	gdev->plnSizeY = vesa->y_resolution;
+
+	gdev->gdfBytesPP = vesa->bits_per_pixel / 8;
+
+	switch (vesa->bits_per_pixel) {
+	case 24:
+		gdev->gdfIndex = GDF_32BIT_X888RGB;
+		break;
+	case 16:
+		gdev->gdfIndex = GDF_16BIT_565RGB;
+		break;
+	default:
+		gdev->gdfIndex = GDF__8BIT_INDEX;
+		break;
+	}
+
+	gdev->isaBase = CONFIG_SYS_ISA_IO_BASE_ADDRESS;
+	gdev->pciBase = vesa->phys_base_ptr;
+
+	gdev->frameAdrs = vesa->phys_base_ptr;
+	gdev->memSize = vesa->bytes_per_scanline * vesa->y_resolution;
+
+	gdev->vprBase = vesa->phys_base_ptr;
+	gdev->cprBase = vesa->phys_base_ptr;
+
+	return 0;
+#else
+	return -ENOSYS;
+#endif
+}
+
+int pci_run_vga_bios(pci_dev_t dev, int (*int15_handler)(void), bool emulate)
+{
+	struct pci_rom_header *rom, *ram;
+	int vesa_mode = -1;
+	uint16_t class;
+	int ret;
+
+	/* Only execute VGA ROMs */
+	pci_read_config_word(dev, PCI_CLASS_DEVICE, &class);
+	if ((class ^ PCI_CLASS_DISPLAY_VGA) & 0xff00) {
+		debug("%s: Class %#x, should be %#x\n", __func__, class,
+		      PCI_CLASS_DISPLAY_VGA);
+		return -ENODEV;
+	}
+
+	if (!should_load_oprom(dev))
+		return -ENXIO;
+
+	ret = pci_rom_probe(dev, class, &rom);
+	if (ret)
+		return ret;
+
+	ret = pci_rom_load(class, rom, &ram);
+	if (ret)
+		return ret;
+
+	if (!board_should_run_oprom(dev))
+		return -ENXIO;
+
+#if defined(CONFIG_FRAMEBUFFER_SET_VESA_MODE) && \
+		defined(CONFIG_FRAMEBUFFER_VESA_MODE)
+	vesa_mode = CONFIG_FRAMEBUFFER_VESA_MODE;
+#endif
+	if (emulate) {
+#ifdef CONFIG_BIOSEMU
+		BE_VGAInfo *info;
+
+		ret = biosemu_setup(dev, &info);
+		if (ret)
+			return ret;
+		biosemu_set_interrupt_handler(0x15, int15_handler);
+		ret = biosemu_run(dev, (uchar *)ram, 1 << 16, info, true,
+				  vesa_mode, &mode_info);
+		if (ret)
+			return ret;
+#else
+		printf("BIOS emulation not available - see CONFIG_BIOSEMU\n");
+		return -ENOSYS;
+#endif
+	} else {
+#ifdef CONFIG_X86
+		bios_set_interrupt_handler(0x15, int15_handler);
+
+		bios_run_on_x86(dev, (unsigned long)ram, vesa_mode,
+				&mode_info);
+#else
+		printf("BIOS native execution is only available on x86\n");
+		return -ENOSYS;
+#endif
+	}
+
+	return 0;
+}
diff --git a/include/bios_emul.h b/include/bios_emul.h
index 35d1ff3..a11d4e4 100644
--- a/include/bios_emul.h
+++ b/include/bios_emul.h
@@ -40,4 +40,18 @@ struct vbe_mode_info;
 
 int BootVideoCardBIOS(pci_dev_t pcidev, BE_VGAInfo **pVGAInfo, int cleanUp);
 
+/* Run a BIOS ROM natively (only supported on x86 machines) */
+void bios_run_on_x86(pci_dev_t pcidev, unsigned long addr, int vesa_mode,
+		     struct vbe_mode_info *mode_info);
+
+/**
+ * bios_set_interrupt_handler() - Install an interrupt handler for the BIOS
+ *
+ * This installs an interrupt handler that the BIOS will call when needed.
+ *
+ * @intnum:		Interrupt number to install a handler for
+ * @int_handler_func:	Function to call to handle interrupt
+ */
+void bios_set_interrupt_handler(int intnum, int (*int_handler_func)(void));
+
 #endif
diff --git a/include/pci_rom.h b/include/pci_rom.h
new file mode 100644
index 0000000..8b2674c
--- /dev/null
+++ b/include/pci_rom.h
@@ -0,0 +1,58 @@
+/*
+ * From coreboot file of same name
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _PCI_ROM_H
+#define _PCI_ROM_H
+
+#define PCI_ROM_HDR			0xaa55
+#define PCI_VGA_RAM_IMAGE_START		0xc0000
+
+struct pci_rom_header {
+	uint16_t signature;
+	uint8_t size;
+	uint8_t init[3];
+	uint8_t reserved[0x12];
+	uint16_t data;
+};
+
+struct pci_rom_data {
+	uint32_t signature;
+	uint16_t vendor;
+	uint16_t device;
+	uint16_t reserved_1;
+	uint16_t dlen;
+	uint8_t drevision;
+	uint8_t class_lo;
+	uint16_t class_hi;
+	uint16_t ilen;
+	uint16_t irevision;
+	uint8_t type;
+	uint8_t indicator;
+	uint16_t reserved_2;
+};
+
+ /**
+ * pci_run_vga_bios() - Run the VGA BIOS in an x86 PC
+ *
+ * @dev:	Video device containing the BIOS
+ * @int15_handler:	Function to call to handle int 0x15
+ * @emulate:	true to use the x86 emulator, false to run native
+ */
+int pci_run_vga_bios(pci_dev_t dev, int (*int15_handler)(void), bool emulate);
+
+/**
+ * board_map_oprom_vendev() - map several PCI IDs to the one the ROM expects
+ *
+ * Some VGA option roms are used for several chipsets but they only have one
+ * PCI ID in their header. If we encounter such an option rom, we need to do
+ * the mapping ourselves.
+ *
+ * @vendev:	Vendor and device for the video device
+ * @return standard vendor and device expected by the ROM
+ */
+uint32_t board_map_oprom_vendev(uint32_t vendev);
+
+#endif
-- 
2.1.0.rc2.206.gedb03e5



More information about the U-Boot mailing list