#!/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: 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 " \ " []" % 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()