check in all bootmanager sources
[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..213ac00
--- /dev/null
@@ -0,0 +1,256 @@
+#!/usr/bin/env python
+
+"""
+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
+
+
+
+class merge_hw_tables:
+    
+    def merge_files(self, 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 )
+            return
+
+
+        # associative array to store all matches of module -> ['vendor:device',..]
+        # entries
+        all_modules= {}
+
+
+        # 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_name= parts[len(parts)-1]
+            module_name_len= len(module_name)
+            if module_name[module_name_len-3:] == ".ko":
+                all_modules[module_name[:-3]]= []
+
+
+        # 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)\n" % line_num )
+                continue
+
+            # first two parts are always vendor / device id
+            module= line_parts[0]
+            vendor_id= line_parts[1]
+            device_id= line_parts[2]
+
+
+            # valid vendor and devices are 10 chars (0xXXXXXXXX) and begin with 0x
+            if len(vendor_id) != 10 or len(device_id) != 10:
+                sys.stderr.write( "Skipping line %d in pcimap " \
+                                  "(invalid vendor/device id length)\n" %
+                                  line_num )
+                continue
+            
+            if string.lower(vendor_id[:2]) != "0x" \
+                   or string.lower(device_id[:2]) != "0x":
+                sys.stderr.write( "Skipping line %d in pcimap " \
+                                  "(invalid vendor/device id format)\n" % line_num )
+                continue
+
+            # cut down the ids, only need last 4 bytes
+            # start at 6 = (10 total chars - 4 last chars need)
+            vendor_id= string.lower(vendor_id[6:])
+            device_id= string.lower(device_id[6:])
+            
+            full_id= "%s:%s" % (vendor_id, device_id)
+            
+            if all_modules.has_key(module):
+                if full_id not in all_modules[module]:
+                    all_modules[module].append( full_id )
+            else:
+                # normally shouldn't get here, as the list is
+                # prepopulated with all the built modules
+                all_modules[module]= [full_id,]
+
+
+        # parse pcitable, add any more ids for the devices
+        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)
+            vendor_id= string.lower(line_parts[0][2:])
+            device_id= string.lower(line_parts[1][2:])
+            
+            full_id= "%s:%s" % (vendor_id, device_id)
+            
+            # 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]
+            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
+            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]
+            
+            # now add it if we don't already have this module -> id mapping
+            if all_modules.has_key(module):
+                if full_id not in all_modules[module]:
+                    all_modules[module].append( full_id )
+            else:
+                # don't add any modules from pcitable that we don't
+                # already know about
+                pass
+
+        pcitable_file.close()
+        pcimap_file.close()
+        modulesdep_file.close()
+
+        return all_modules
+    
+
+
+if __name__ == "__main__":
+    def usage():
+        print( "\nUsage:" )
+        print( "rewrite-pcitable.py <modules.dep> <modules.pcimap> " \
+               "<pcitable> [<output>]" )
+        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_modules= merge_hw_tables().merge_files( sys.argv[1], sys.argv[2],
+                                                sys.argv[3] )
+
+    if all_modules is not None:
+        for module in all_modules.keys():
+            devices= all_modules[module]
+            if len(devices) > 0:
+                devices_str= string.join( all_modules[module], " " )
+                output_file.write( "%s %s\n" % (module,devices_str) )
+    else:
+        sys.stderr.write( "Unable to list modules.\n" )
+
+    output_file.close()