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