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