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