Rewrote merge_files function to handle case when multiple modules claim
[bootmanager.git] / source / merge_hw_tables.py
1 #!/usr/bin/env python
2
3 """
4 The point of this small utility is to take a file in the format
5 of /lib/modules/`uname -r`/modules.pcimap and /usr/share/hwdata/pcitable
6 and output a condensed, more easily used format for module detection. This is
7 done by first getting a list of all the built modules, then loading the
8 pci ids for each of those modules from modules.pcimap, then finally merging
9 in the contents of pcitable (for built modules only). The result should be
10 a file with a pretty comprehensive mapping of pci ids to module names.
11
12 The output is used by the PlanetLab boot cd (3.0+) and the pl_hwinit script
13 to load all the applicable modules by scanning lspci output.
14
15
16
17 Expected format of file modules.dep includes lines of:
18
19 /full/module/path/mod.ko: <dependencies>
20
21 Expected format of file modules.pcimap includes lines of:
22
23 # pci_module vendor device subvendor subdevice class class_mask driver_data
24 cciss 0x00000e11 0x0000b060 0x00000e11 0x00004070 0x00000000 0x00000000 0x0
25 cciss 0x00000e11 0x0000b178 0x00000e11 0x00004070 0x00000000 0x00000000 0x0
26
27 Expected format of file pcitable includes lines of:
28
29 # ("%d\t%d\t%s\t"%s"\n", vendid, devid, moduleName, cardDescription)
30 # or ("%d\t%d\t%d\t%d\t%s\t"%s"\n", vendid, devid, subvendid, subdevid, moduleNa
31 # me, cardDescription)
32 0x0e11  0x0508  "tmspci"        "Compaq|Netelligent 4/16 Token Ring"
33 0x1000  0x0407  0x8086  0x0532  "megaraid"      "Storage RAID Controller SRCU42X"
34
35 Lines listing a module name of ignore or unknown from pcitable are skipped
36
37
38
39 Output format, for each line that matches the above lines:
40 cciss 0e11:b060 0e11:b178
41
42 """
43
44 import os, sys
45 import string
46
47 PCI_ANY = 0xffffffffL
48
49 def merge_files(modules_dep_path, modules_pcimap_path, pcitable_path):
50     """
51     merge the three files as described above, and return a dictionary.
52     keys are module names, value is a list of pci ids for that module,
53     in the form "0e11:b178"
54     """
55
56     try:
57         modulesdep_file= file(modules_dep_path,"r")
58     except IOError:
59         sys.stderr.write( "Unable to open modules.dep: %s\n" %
60                           modules_dep_path )
61         return
62
63     try:
64         pcimap_file= file(modules_pcimap_path,"r")
65     except IOError:
66         sys.stderr.write( "Unable to open modules.pcimap: %s\n" %
67                           modules_pcimap_path )
68         return
69
70     try:
71         pcitable_file= file(pcitable_path,"r")
72     except IOError:
73         sys.stderr.write( "Unable to open pcitable: %s\n" %
74                           pcitable_path )
75         return
76
77     # associative array to store all matches of module -> ['vendor:device',..]
78     # entries
79     all_modules= {}
80     all_pci_ids= {}
81
82     # first step, create an associative array of all the built modules
83     for line in modulesdep_file:
84         parts= string.split(line,":")
85         if len(parts) < 2:
86             continue
87
88         full_mod_path= parts[0]
89         parts= string.split(full_mod_path,"/")
90         module= parts[len(parts)-1]
91         module_len= len(module)
92         if module[module_len-3:] == ".ko":
93             module= module[:-3]
94             all_modules[module]= []
95
96     modulesdep_file.close()
97
98     # now, parse the pcimap and add devices
99     line_num= 0
100     for line in pcimap_file:
101         line_num= line_num+1
102
103         # skip blank lines, or lines that begin with # (comments)
104         line= string.strip(line)
105         if len(line) == 0:
106             continue
107
108         if line[0] == "#":
109             continue
110
111         line_parts= string.split(line)
112         if line_parts is None or len(line_parts) != 8:
113             sys.stderr.write( "Skipping line %d in pcimap " \
114                               "(incorrect format %s)\n" % (line_num,line) )
115             continue
116
117         # first two parts are always vendor / device id
118         module= line_parts[0]
119
120         try:
121             vendor_id= long(line_parts[1],16)
122         except ValueError, e:
123             sys.stderr.write( "Skipping line %d in %s " \
124                               "(incorrect vendor id format %s)\n" % (line_num,modules_pcimap_path,line_parts[1]))
125             continue
126
127         try:
128             device_id= long(line_parts[2],16)
129         except ValueError, e:
130             sys.stderr.write( "Skipping line %d in %s " \
131                               "(incorrect device id format %s)\n" % (line_num,modules_pcimap_path,line_parts[2]))
132             continue
133
134         try:
135             subvendor_id= long(line_parts[3],16)
136         except ValueError, e:
137             sys.stderr.write( "Skipping line %d in %s " \
138                               "(incorrect subvendor id format %s)\n" % (line_num,modules_pcimap_path,line_parts[3]))
139             continue
140
141         try:
142             subdevice_id= long(line_parts[4],16)
143         except ValueError, e:
144             sys.stderr.write( "Skipping line %d in %s " \
145                               "(incorrect subdevice id format %s)\n" % (line_num,modules_pcimap_path,line_parts[4]))
146             continue
147
148         full_id= (vendor_id, device_id, subvendor_id, subdevice_id)
149         if not all_modules.has_key(module):
150             # normally shouldn't get here, as the list is
151             # prepopulated with all the built modules
152
153             # XXX we probably shouldn't be doing this at all
154             all_modules[module] = [full_id]
155         else:
156             all_modules[module].append(full_id)
157             
158         if all_pci_ids.has_key(full_id):
159             # conflict as there are multiple modules that support
160             # particular pci device
161             all_pci_ids[full_id].append(module)
162         else:
163             all_pci_ids[full_id]= [module]
164
165     pcimap_file.close()
166
167     # parse pcitable, add any more ids for the devices
168     # We make the (potentially risky) assumption that pcitable contains
169     # only unique (vendor,device,subvendor,subdevice) entries.
170     line_num= 0
171     for line in pcitable_file:
172         line_num= line_num+1
173
174         # skip blank lines, or lines that begin with # (comments)
175         line= string.strip(line)
176         if len(line) == 0:
177             continue
178
179         if line[0] == "#":
180             continue
181
182         line_parts= string.split(line)
183         if line_parts is None or len(line_parts) <= 2:
184             sys.stderr.write( "Skipping line %d in pcitable " \
185                               "(incorrect format 1)\n" % line_num )
186             continue
187
188         # vendor id is always the first field, device the second. also,
189         # strip off first two chars (the 0x)
190         try:
191             vendor_id= long(line_parts[0],16)
192         except ValueError, e:
193             sys.stderr.write( "Skipping vendor_id %s in %s on line %d\n" \
194                               % (line_parts[0],pcitable_path,line_num))
195             continue
196
197         try:
198             device_id= long(line_parts[1],16)
199         except ValueError, e:
200             sys.stderr.write( "Skipping device %s in %s on line %d\n" \
201                               % (line_parts[1],pcitable_path,line_num))
202             continue
203
204         # if the first char of the third field is a double
205         # quote, the third field is a module, else if the first
206         # char of the third field is a 0 (zero), the fifth field is
207         # the module name. it would nice if there was any easy way
208         # to split a string on spaces, but recognize quoted strings,
209         # so they wouldn't be split up. that is the reason for this wierd check
210         if line_parts[2][0] == '"':
211             module= line_parts[2]
212
213             subvendor_id=PCI_ANY
214             subdevice_id=PCI_ANY
215         elif line_parts[2][0] == '0':
216             try:
217                 module= line_parts[4]
218             except ValueError, e:
219                 sys.stderr.write( "Skipping line %d in pcitable " \
220                                   "(incorrect format 2)\n" % line_num )
221                 continue
222             try:
223                 subvendor_id= long(line_parts[2],16)
224             except ValueError, e:
225                 sys.stderr.write( "Skipping line %d in pcitable " \
226                                   "(incorrect format 2a)\n" % line_num )
227                 continue
228             
229             try:
230                 subdevice_id= long(line_parts[3],16)
231             except ValueError, e:
232                 sys.stderr.write( "Skipping line %d in pcitable " \
233                                   "(incorrect format 2b)\n" % line_num )
234
235         else:
236             sys.stderr.write( "Skipping line %d in pcitable " \
237                               "(incorrect format 3)\n" % line_num )
238             continue
239
240         # remove the first and last char of module (quote marks)
241         module= module[1:]
242         module= module[:len(module)-1]
243
244         full_id= (vendor_id, device_id, subvendor_id, subdevice_id)
245
246         if not all_modules.has_key(module):
247             # Do not process any modules listed in pcitable for which
248             # we do not have a prebuilt module.
249             continue
250
251         if not full_id in all_modules[module]:
252             all_modules[module].append(full_id)
253
254         if all_pci_ids.has_key(full_id):
255             if not module in all_pci_ids[full_id]:
256                 all_pci_ids[full_id].append(module)
257             
258             # check if there are duplicate mappings between modules
259             # and full_ids
260             if len(all_pci_ids[full_id])>1:
261                 # collect the set of modules that are different than what
262                 # is listed in the pcitables file
263                 other_modules = []
264                 for other_module in all_pci_ids[full_id]:
265                     if other_module != module:
266                         other_modules.append(other_module)
267
268                 # remove full_id from the set of other modules in all_modules {}
269                 for other_module in other_modules:
270                     all_modules[other_module].remove(full_id)
271
272                 # ensure that there is only one full_id -> module 
273                 all_pci_ids[full_id] = [module]
274
275         else:
276             all_pci_ids[full_id] = [module]
277                 
278     pcitable_file.close()
279
280     return (all_pci_ids,all_modules)
281
282 if __name__ == "__main__":
283     def usage():
284         print( "\nUsage:" )
285         print( "%s <modules.dep> <modules.pcimap> " \
286                "<pcitable> [<output>]" % sys.argv[0] )
287         print( "" )
288         
289     if len(sys.argv) < 4:
290         usage()
291         sys.exit(1)
292
293
294     if len(sys.argv) > 4:
295         output_file_name= sys.argv[4]
296         try:
297             output_file= file(output_file_name,"w")
298         except IOError:
299             sys.stderr.write( "Unable to open %s for writing.\n" % output_file )
300             sys.exit(1)
301     else:
302         output_file= sys.stdout
303
304
305     (all_pci_ids,all_modules)=merge_files( sys.argv[1],
306                                            sys.argv[2],
307                                            sys.argv[3] )
308     if all_modules is not None:
309         for module in all_modules.keys():
310             pci_ids = all_modules[module]
311             if len(pci_ids)>0:
312                 output_file.write("%s" % module)
313                 for pci_id in pci_ids:
314                     output_file.write(" %x:%x:%x:%x" % pci_id)
315                 output_file.write(" \n")
316     else:
317         sys.stderr.write( "Unable to list modules.\n" )
318
319     output_file.close()