- In kernel versions <2.6.14, mptscsih is the actual module that should
[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         # XXX In kernel versions <2.6.14, mptscsih is the actual
121         # module that should be loaded instead of mptbase.
122         if module == "mptbase":
123             module= "mptscsih"
124
125         try:
126             vendor_id= long(line_parts[1],16)
127         except ValueError, e:
128             sys.stderr.write( "Skipping line %d in %s " \
129                               "(incorrect vendor id format %s)\n" % (line_num,modules_pcimap_path,line_parts[1]))
130             continue
131
132         try:
133             device_id= long(line_parts[2],16)
134         except ValueError, e:
135             sys.stderr.write( "Skipping line %d in %s " \
136                               "(incorrect device id format %s)\n" % (line_num,modules_pcimap_path,line_parts[2]))
137             continue
138
139         try:
140             subvendor_id= long(line_parts[3],16)
141         except ValueError, e:
142             sys.stderr.write( "Skipping line %d in %s " \
143                               "(incorrect subvendor id format %s)\n" % (line_num,modules_pcimap_path,line_parts[3]))
144             continue
145
146         try:
147             subdevice_id= long(line_parts[4],16)
148         except ValueError, e:
149             sys.stderr.write( "Skipping line %d in %s " \
150                               "(incorrect subdevice id format %s)\n" % (line_num,modules_pcimap_path,line_parts[4]))
151             continue
152
153         full_id= (vendor_id, device_id, subvendor_id, subdevice_id)
154         if not all_modules.has_key(module):
155             # normally shouldn't get here, as the list is
156             # prepopulated with all the built modules
157
158             # XXX we probably shouldn't be doing this at all
159             all_modules[module] = [full_id]
160         else:
161             all_modules[module].append(full_id)
162             
163         if all_pci_ids.has_key(full_id):
164             # conflict as there are multiple modules that support
165             # particular pci device
166             all_pci_ids[full_id].append(module)
167         else:
168             all_pci_ids[full_id]= [module]
169
170     pcimap_file.close()
171
172     # parse pcitable, add any more ids for the devices
173     # We make the (potentially risky) assumption that pcitable contains
174     # only unique (vendor,device,subvendor,subdevice) entries.
175     line_num= 0
176     for line in pcitable_file:
177         line_num= line_num+1
178
179         # skip blank lines, or lines that begin with # (comments)
180         line= string.strip(line)
181         if len(line) == 0:
182             continue
183
184         if line[0] == "#":
185             continue
186
187         line_parts= string.split(line)
188         if line_parts is None or len(line_parts) <= 2:
189             sys.stderr.write( "Skipping line %d in pcitable " \
190                               "(incorrect format 1)\n" % line_num )
191             continue
192
193         # vendor id is always the first field, device the second. also,
194         # strip off first two chars (the 0x)
195         try:
196             vendor_id= long(line_parts[0],16)
197         except ValueError, e:
198             sys.stderr.write( "Skipping vendor_id %s in %s on line %d\n" \
199                               % (line_parts[0],pcitable_path,line_num))
200             continue
201
202         try:
203             device_id= long(line_parts[1],16)
204         except ValueError, e:
205             sys.stderr.write( "Skipping device %s in %s on line %d\n" \
206                               % (line_parts[1],pcitable_path,line_num))
207             continue
208
209         # if the first char of the third field is a double
210         # quote, the third field is a module, else if the first
211         # char of the third field is a 0 (zero), the fifth field is
212         # the module name. it would nice if there was any easy way
213         # to split a string on spaces, but recognize quoted strings,
214         # so they wouldn't be split up. that is the reason for this wierd check
215         if line_parts[2][0] == '"':
216             module= line_parts[2]
217
218             subvendor_id=PCI_ANY
219             subdevice_id=PCI_ANY
220         elif line_parts[2][0] == '0':
221             try:
222                 module= line_parts[4]
223             except ValueError, e:
224                 sys.stderr.write( "Skipping line %d in pcitable " \
225                                   "(incorrect format 2)\n" % line_num )
226                 continue
227             try:
228                 subvendor_id= long(line_parts[2],16)
229             except ValueError, e:
230                 sys.stderr.write( "Skipping line %d in pcitable " \
231                                   "(incorrect format 2a)\n" % line_num )
232                 continue
233             
234             try:
235                 subdevice_id= long(line_parts[3],16)
236             except ValueError, e:
237                 sys.stderr.write( "Skipping line %d in pcitable " \
238                                   "(incorrect format 2b)\n" % line_num )
239
240         else:
241             sys.stderr.write( "Skipping line %d in pcitable " \
242                               "(incorrect format 3)\n" % line_num )
243             continue
244
245         # remove the first and last char of module (quote marks)
246         module= module[1:]
247         module= module[:len(module)-1]
248
249         full_id= (vendor_id, device_id, subvendor_id, subdevice_id)
250
251         if not all_modules.has_key(module):
252             # Do not process any modules listed in pcitable for which
253             # we do not have a prebuilt module.
254             continue
255
256         if not full_id in all_modules[module]:
257             all_modules[module].append(full_id)
258
259         if all_pci_ids.has_key(full_id):
260             if not module in all_pci_ids[full_id]:
261                 all_pci_ids[full_id].append(module)
262             
263             # check if there are duplicate mappings between modules
264             # and full_ids
265             if len(all_pci_ids[full_id])>1:
266                 # collect the set of modules that are different than what
267                 # is listed in the pcitables file
268                 other_modules = []
269                 for other_module in all_pci_ids[full_id]:
270                     if other_module != module:
271                         other_modules.append(other_module)
272
273                 # remove full_id from the set of other modules in all_modules {}
274                 for other_module in other_modules:
275                     all_modules[other_module].remove(full_id)
276
277                 # ensure that there is only one full_id -> module 
278                 all_pci_ids[full_id] = [module]
279
280         else:
281             all_pci_ids[full_id] = [module]
282                 
283     pcitable_file.close()
284
285     return (all_pci_ids,all_modules)
286
287 if __name__ == "__main__":
288     def usage():
289         print( "\nUsage:" )
290         print( "%s <modules.dep> <modules.pcimap> " \
291                "<pcitable> [<output>]" % sys.argv[0] )
292         print( "" )
293         
294     if len(sys.argv) < 4:
295         usage()
296         sys.exit(1)
297
298
299     if len(sys.argv) > 4:
300         output_file_name= sys.argv[4]
301         try:
302             output_file= file(output_file_name,"w")
303         except IOError:
304             sys.stderr.write( "Unable to open %s for writing.\n" % output_file )
305             sys.exit(1)
306     else:
307         output_file= sys.stdout
308
309
310     (all_pci_ids,all_modules)=merge_files( sys.argv[1],
311                                            sys.argv[2],
312                                            sys.argv[3] )
313     if all_modules is not None:
314         for module in all_modules.keys():
315             pci_ids = all_modules[module]
316             if len(pci_ids)>0:
317                 output_file.write("%s" % module)
318                 for pci_id in pci_ids:
319                     output_file.write(" %x:%x:%x:%x" % pci_id)
320                 output_file.write(" \n")
321     else:
322         sys.stderr.write( "Unable to list modules.\n" )
323
324     output_file.close()