Adds backward compatibility to the BootManager for older bootcds. Rather than
[bootmanager.git] / source / merge_hw_tables.py
diff --git a/source/merge_hw_tables.py b/source/merge_hw_tables.py
new file mode 100755 (executable)
index 0000000..dfe3378
--- /dev/null
@@ -0,0 +1,332 @@
+#!/usr/bin/python2
+
+# Copyright (c) 2003 Intel Corporation
+# All rights reserved.
+#
+# Copyright (c) 2004-2006 The Trustees of Princeton University
+# All rights reserved.
+
+
+"""
+The point of this small utility is to take a file in the format
+of /lib/modules/`uname -r`/modules.pcimap and /usr/share/hwdata/pcitable
+and output a condensed, more easily used format for module detection. This is
+done by first getting a list of all the built modules, then loading the
+pci ids for each of those modules from modules.pcimap, then finally merging
+in the contents of pcitable (for built modules only). The result should be
+a file with a pretty comprehensive mapping of pci ids to module names.
+
+The output is used by the PlanetLab boot cd (3.0+) and the pl_hwinit script
+to load all the applicable modules by scanning lspci output.
+
+
+
+Expected format of file modules.dep includes lines of:
+
+/full/module/path/mod.ko: <dependencies>
+
+Expected format of file modules.pcimap includes lines of:
+
+# pci_module vendor device subvendor subdevice class class_mask driver_data
+cciss 0x00000e11 0x0000b060 0x00000e11 0x00004070 0x00000000 0x00000000 0x0
+cciss 0x00000e11 0x0000b178 0x00000e11 0x00004070 0x00000000 0x00000000 0x0
+
+Expected format of file pcitable includes lines of:
+
+# ("%d\t%d\t%s\t"%s"\n", vendid, devid, moduleName, cardDescription)
+# or ("%d\t%d\t%d\t%d\t%s\t"%s"\n", vendid, devid, subvendid, subdevid, moduleNa
+# me, cardDescription)
+0x0e11  0x0508  "tmspci"        "Compaq|Netelligent 4/16 Token Ring"
+0x1000  0x0407  0x8086  0x0532  "megaraid"      "Storage RAID Controller SRCU42X"
+
+Lines listing a module name of ignore or unknown from pcitable are skipped
+
+
+
+Output format, for each line that matches the above lines:
+cciss 0e11:b060 0e11:b178
+
+"""
+
+import os, sys
+import string
+import StringIO
+
+PCI_ANY = 0xffffffffL
+
+def merge_files(modules_dep_path, modules_pcimap_path, pcitable_path):
+    """
+    merge the three files as described above, and return a dictionary.
+    keys are module names, value is a list of pci ids for that module,
+    in the form "0e11:b178"
+    """
+
+    try:
+        modulesdep_file= file(modules_dep_path,"r")
+    except IOError:
+        sys.stderr.write( "Unable to open modules.dep: %s\n" %
+                          modules_dep_path )
+        return
+
+    try:
+        pcimap_file= file(modules_pcimap_path,"r")
+    except IOError:
+        sys.stderr.write( "Unable to open modules.pcimap: %s\n" %
+                          modules_pcimap_path )
+        return
+
+    try:
+        pcitable_file= file(pcitable_path,"r")
+    except IOError:
+        sys.stderr.write( "Unable to open pcitable: %s\n" %
+                          pcitable_path )
+       pcitable_file=StringIO.StringIO()
+
+    # associative array to store all matches of module -> ['vendor:device',..]
+    # entries
+    all_modules= {}
+    all_pci_ids= {}
+
+    # first step, create an associative array of all the built modules
+    for line in modulesdep_file:
+        parts= string.split(line,":")
+        if len(parts) < 2:
+            continue
+
+        full_mod_path= parts[0]
+        parts= string.split(full_mod_path,"/")
+        module= parts[len(parts)-1]
+        module_len= len(module)
+        if module[module_len-3:] == ".ko":
+            module= module[:-3]
+            all_modules[module]= []
+
+    modulesdep_file.close()
+
+    # now, parse the pcimap and add devices
+    line_num= 0
+    for line in pcimap_file:
+        line_num= line_num+1
+
+        # skip blank lines, or lines that begin with # (comments)
+        line= string.strip(line)
+        if len(line) == 0:
+            continue
+
+        if line[0] == "#":
+            continue
+
+        line_parts= string.split(line)
+        if line_parts is None or len(line_parts) != 8:
+            sys.stderr.write( "Skipping line %d in pcimap " \
+                              "(incorrect format %s)\n" % (line_num,line) )
+            continue
+
+        # first two parts are always vendor / device id
+        module= line_parts[0]
+
+        # XXX In kernel versions <2.6.14, mptscsih is the actual
+        # module that should be loaded instead of mptbase.
+        if module == "mptbase":
+            module= "mptscsih"
+
+        try:
+            vendor_id= long(line_parts[1],16)
+        except ValueError, e:
+            sys.stderr.write( "Skipping line %d in %s " \
+                              "(incorrect vendor id format %s)\n" % (line_num,modules_pcimap_path,line_parts[1]))
+            continue
+
+        try:
+            device_id= long(line_parts[2],16)
+        except ValueError, e:
+            sys.stderr.write( "Skipping line %d in %s " \
+                              "(incorrect device id format %s)\n" % (line_num,modules_pcimap_path,line_parts[2]))
+            continue
+
+        try:
+            subvendor_id= long(line_parts[3],16)
+        except ValueError, e:
+            sys.stderr.write( "Skipping line %d in %s " \
+                              "(incorrect subvendor id format %s)\n" % (line_num,modules_pcimap_path,line_parts[3]))
+            continue
+
+        try:
+            subdevice_id= long(line_parts[4],16)
+        except ValueError, e:
+            sys.stderr.write( "Skipping line %d in %s " \
+                              "(incorrect subdevice id format %s)\n" % (line_num,modules_pcimap_path,line_parts[4]))
+            continue
+
+        full_id= (vendor_id, device_id, subvendor_id, subdevice_id)
+        if not all_modules.has_key(module):
+            # normally shouldn't get here, as the list is
+            # prepopulated with all the built modules
+
+            # XXX we probably shouldn't be doing this at all
+            all_modules[module] = [full_id]
+        else:
+            all_modules[module].append(full_id)
+            
+        if all_pci_ids.has_key(full_id):
+            # conflict as there are multiple modules that support
+            # particular pci device
+            all_pci_ids[full_id].append(module)
+        else:
+            all_pci_ids[full_id]= [module]
+
+    pcimap_file.close()
+
+    # parse pcitable, add any more ids for the devices
+    # We make the (potentially risky) assumption that pcitable contains
+    # only unique (vendor,device,subvendor,subdevice) entries.
+    line_num= 0
+    for line in pcitable_file:
+        line_num= line_num+1
+
+        # skip blank lines, or lines that begin with # (comments)
+        line= string.strip(line)
+        if len(line) == 0:
+            continue
+
+        if line[0] == "#":
+            continue
+
+        line_parts= string.split(line)
+        if line_parts is None or len(line_parts) <= 2:
+            sys.stderr.write( "Skipping line %d in pcitable " \
+                              "(incorrect format 1)\n" % line_num )
+            continue
+
+        # vendor id is always the first field, device the second. also,
+        # strip off first two chars (the 0x)
+        try:
+            vendor_id= long(line_parts[0],16)
+        except ValueError, e:
+            sys.stderr.write( "Skipping vendor_id %s in %s on line %d\n" \
+                              % (line_parts[0],pcitable_path,line_num))
+            continue
+
+        try:
+            device_id= long(line_parts[1],16)
+        except ValueError, e:
+            sys.stderr.write( "Skipping device %s in %s on line %d\n" \
+                              % (line_parts[1],pcitable_path,line_num))
+            continue
+
+        # if the first char of the third field is a double
+        # quote, the third field is a module, else if the first
+        # char of the third field is a 0 (zero), the fifth field is
+        # the module name. it would nice if there was any easy way
+        # to split a string on spaces, but recognize quoted strings,
+        # so they wouldn't be split up. that is the reason for this wierd check
+        if line_parts[2][0] == '"':
+            module= line_parts[2]
+
+            subvendor_id=PCI_ANY
+            subdevice_id=PCI_ANY
+        elif line_parts[2][0] == '0':
+            try:
+                module= line_parts[4]
+            except ValueError, e:
+                sys.stderr.write( "Skipping line %d in pcitable " \
+                                  "(incorrect format 2)\n" % line_num )
+                continue
+            try:
+                subvendor_id= long(line_parts[2],16)
+            except ValueError, e:
+                sys.stderr.write( "Skipping line %d in pcitable " \
+                                  "(incorrect format 2a)\n" % line_num )
+                continue
+            
+            try:
+                subdevice_id= long(line_parts[3],16)
+            except ValueError, e:
+                sys.stderr.write( "Skipping line %d in pcitable " \
+                                  "(incorrect format 2b)\n" % line_num )
+
+        else:
+            sys.stderr.write( "Skipping line %d in pcitable " \
+                              "(incorrect format 3)\n" % line_num )
+            continue
+
+        # remove the first and last char of module (quote marks)
+        module= module[1:]
+        module= module[:len(module)-1]
+
+        full_id= (vendor_id, device_id, subvendor_id, subdevice_id)
+
+        if not all_modules.has_key(module):
+            # Do not process any modules listed in pcitable for which
+            # we do not have a prebuilt module.
+            continue
+
+        if not full_id in all_modules[module]:
+            all_modules[module].append(full_id)
+
+        if all_pci_ids.has_key(full_id):
+            if not module in all_pci_ids[full_id]:
+                all_pci_ids[full_id].append(module)
+            
+            # check if there are duplicate mappings between modules
+            # and full_ids
+            if len(all_pci_ids[full_id])>1:
+                # collect the set of modules that are different than what
+                # is listed in the pcitables file
+                other_modules = []
+                for other_module in all_pci_ids[full_id]:
+                    if other_module != module:
+                        other_modules.append(other_module)
+
+                # remove full_id from the set of other modules in all_modules {}
+                for other_module in other_modules:
+                    all_modules[other_module].remove(full_id)
+
+                # ensure that there is only one full_id -> module 
+                all_pci_ids[full_id] = [module]
+
+        else:
+            all_pci_ids[full_id] = [module]
+                
+    pcitable_file.close()
+
+    return (all_pci_ids,all_modules)
+
+if __name__ == "__main__":
+    def usage():
+        print( "\nUsage:" )
+        print( "%s <modules.dep> <modules.pcimap> " \
+               "<pcitable> [<output>]" % sys.argv[0] )
+        print( "" )
+        
+    if len(sys.argv) < 4:
+        usage()
+        sys.exit(1)
+
+
+    if len(sys.argv) > 4:
+        output_file_name= sys.argv[4]
+        try:
+            output_file= file(output_file_name,"w")
+        except IOError:
+            sys.stderr.write( "Unable to open %s for writing.\n" % output_file )
+            sys.exit(1)
+    else:
+        output_file= sys.stdout
+
+
+    (all_pci_ids,all_modules)=merge_files( sys.argv[1],
+                                           sys.argv[2],
+                                           sys.argv[3] )
+    if all_modules is not None:
+        for module in all_modules.keys():
+            pci_ids = all_modules[module]
+            if len(pci_ids)>0:
+                output_file.write("%s" % module)
+                for pci_id in pci_ids:
+                    output_file.write(" %x:%x:%x:%x" % pci_id)
+                output_file.write(" \n")
+    else:
+        sys.stderr.write( "Unable to list modules.\n" )
+
+    output_file.close()