From 907686ae6a41a0cb75d0ff2a565586b6f4e25615 Mon Sep 17 00:00:00 2001
From: Dave Frodin <dave.frodin@se-eng.com>
Date: Thu, 23 May 2013 16:07:17 -0600
Subject: [PATCH] Seabios: allow mapping of multiple PCI option ROMs to one

This feature was added to allow mapping multiple different PCI
vendor/device IDs to a single ID. It allows a coreboot/seabios ROM
image to be used on a mainboard whose graphics ID will vary depending
on which cpu is installed. The intent is to have the coreboot mainboard
define its VGA_BIOS_ID as the ID that is present in the actual VGA BIOS.
The PCI ID of all possible graphics IDs would then be mapped to that ID.
It may have use for other PCI devices where a single option ROM can be
used with PCI devices with varying IDs.

Signed-off-by: Dave Frodin <dave.frodin@se-eng.com>
---
 src/optionroms.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 86 insertions(+), 1 deletion(-)

diff --git a/src/optionroms.c b/src/optionroms.c
index ac92613..3b86244 100644
--- a/src/optionroms.c
+++ b/src/optionroms.c
@@ -177,13 +177,98 @@ deploy_romfile(struct romfile_s *file)
     return rom;
 }
 
+/* Read up to 8 ascii chars plus one non-convertible character from a
+ * string and convert to binary.
+ */
+static u32
+text_to_binary( char **ptr )
+{
+    u32 data = 0;
+    u8 loop = 0;
+    if (*ptr == NULL)
+        return data;
+    for ( ; ; (*ptr)++ ) {
+        if ((**ptr >= '0') && ( **ptr <= '9'))
+            data = (data * 0x10) + (**ptr - '0');
+        else if ((**ptr >= 'a') && (**ptr <= 'f'))
+            data = (data * 0x10) + (**ptr - 'a' + 0x0A);
+        else if ((**ptr >= 'A') && (**ptr <= 'F'))
+            data = (data * 0x10) + (**ptr - 'A' + 0x0A);
+        else
+            break;
+        if (++loop >= 9)
+            break;
+    }
+    (*ptr)++; /* point to the char after the non-convertible char */
+    return data;
+}
+
+/* Allow mapping of multiple different PCI IDs to a single ID. A single AMD
+ * VGA BIOS will quite often be used on hardware that reports different
+ * PCI graphics IDs. This allows a mainboard to have a single definition
+ * (which would match the ID in the option ROM) yet would support multiple
+ * CPU IDs.
+ * Example vendev-map format for AMD Family14 (ID=1002) vgabios.
+ *    10029803-10029802
+ *    10029804-10029802
+ *    10029805-10029802
+ *    10029806-10029802
+ *    10029807-10029802
+ *    10029808-10029802
+ *    10029809-10029802
+ */
+#define MAP_FILE_STATUS_UNINITED 0
+#define MAP_FILE_STATUS_GOOD     1
+#define MAP_FILE_STATUS_NOEXIST  2
+#define MAP_FILE_STATUS_USED     3
+#define MAPPER_BYTES_PER_LINE    18 /* U32 src, '-', U32 dst, 0x0A */
+
+static u32
+map_oprom_vendev(u32 vendev)
+{
+    static char *filedata = NULL;
+    static int filesize = 0, map_status = MAP_FILE_STATUS_UNINITED;
+    u32 vendev_src = 0, vendev_dst = 0;
+    char *tmp_ptr;
+    int i;
+
+    if ((map_status == MAP_FILE_STATUS_NOEXIST) ||
+        (map_status == MAP_FILE_STATUS_USED))
+        return vendev;
+
+    if (map_status == MAP_FILE_STATUS_UNINITED) {
+        filedata = romfile_loadfile("vendev-map", &filesize);
+        if (!filedata) {
+            map_status = MAP_FILE_STATUS_NOEXIST;
+            return vendev;
+        }
+        else
+            map_status = MAP_FILE_STATUS_GOOD;
+    }
+
+    tmp_ptr = filedata;
+    for (i=0; i < filesize/MAPPER_BYTES_PER_LINE; i++) {
+        vendev_src = text_to_binary( &tmp_ptr );
+        vendev_dst = text_to_binary( &tmp_ptr );
+        dprintf(5, "map %d: src=%x dst=%x\n", i, vendev_src, vendev_dst);
+        if ( vendev_src == vendev) {
+            dprintf(1, "Mapping PCI device %8x to %8x\n",vendev, vendev_dst);
+            map_status = MAP_FILE_STATUS_USED;
+            free(filedata);
+            return vendev_dst;
+        }
+    }
+    return vendev;
+}
+
 // Check if an option rom is at a hardcoded location or in CBFS.
 static struct rom_header *
 lookup_hardcode(struct pci_device *pci)
 {
     char fname[17];
+    u32 vendev = map_oprom_vendev((pci->vendor << 16) | pci->device);
     snprintf(fname, sizeof(fname), "pci%04x,%04x.rom"
-             , pci->vendor, pci->device);
+             , vendev >> 16, vendev & 0xFFFF);
     struct romfile_s *file = romfile_find(fname);
     if (file)
         return deploy_romfile(file);
-- 
1.8.1.2

