debuggers.hg

view tools/python/xen/util/pci.py @ 17989:e65fe28b5288

XenAPI: Add Physical PCI Device (PPCI) Support

Signed-off-by: Yosuke Iwamatsu <y-iwamatsu@ab.jp.nec.com>
author Keir Fraser <keir.fraser@citrix.com>
date Thu Jul 03 10:26:16 2008 +0100 (2008-07-03)
parents 9d2a45d4b6c6
children 2463e2ef602f
line source
1 #!/usr/bin/env python
2 #
3 # PCI Device Information Class
4 # - Helps obtain information about which I/O resources a PCI device needs
5 #
6 # Author: Ryan Wilson <hap9@epoch.ncsc.mil>
8 import sys
9 import os, os.path
10 import resource
11 import re
12 import types
14 PROC_MNT_PATH = '/proc/mounts'
15 PROC_PCI_PATH = '/proc/bus/pci/devices'
16 PROC_PCI_NUM_RESOURCES = 7
18 SYSFS_PCI_DEVS_PATH = '/bus/pci/devices'
19 SYSFS_PCI_DEV_RESOURCE_PATH = '/resource'
20 SYSFS_PCI_DEV_CONFIG_PATH = '/config'
21 SYSFS_PCI_DEV_IRQ_PATH = '/irq'
22 SYSFS_PCI_DEV_DRIVER_DIR_PATH = '/driver'
23 SYSFS_PCI_DEV_VENDOR_PATH = '/vendor'
24 SYSFS_PCI_DEV_DEVICE_PATH = '/device'
25 SYSFS_PCI_DEV_SUBVENDOR_PATH = '/subsystem_vendor'
26 SYSFS_PCI_DEV_SUBDEVICE_PATH = '/subsystem_device'
27 SYSFS_PCI_DEV_CLASS_PATH = '/class'
29 LSPCI_CMD = 'lspci'
31 PCI_BAR_IO = 0x01
32 PCI_BAR_IO_MASK = ~0x03
33 PCI_BAR_MEM_MASK = ~0x0f
34 PCI_STATUS_CAP_MASK = 0x10
35 PCI_STATUS_OFFSET = 0x6
36 PCI_CAP_OFFSET = 0x34
37 MSIX_BIR_MASK = 0x7
38 MSIX_SIZE_MASK = 0x7ff
40 # Global variable to store information from lspci
41 lspci_info = None
43 #Calculate PAGE_SHIFT: number of bits to shift an address to get the page number
44 PAGE_SIZE = resource.getpagesize()
45 PAGE_SHIFT = 0
46 t = PAGE_SIZE
47 while not (t&1):
48 t>>=1
49 PAGE_SHIFT+=1
51 PAGE_MASK=~(PAGE_SIZE - 1)
52 # Definitions from Linux: include/linux/pci.h
53 def PCI_DEVFN(slot, func):
54 return ((((slot) & 0x1f) << 3) | ((func) & 0x07))
56 def parse_hex(val):
57 try:
58 if isinstance(val, types.StringTypes):
59 return int(val, 16)
60 else:
61 return val
62 except ValueError:
63 return None
65 def find_sysfs_mnt():
66 mounts_file = open(PROC_MNT_PATH,'r')
68 for line in mounts_file:
69 sline = line.split()
70 if len(sline)<3:
71 continue
73 if sline[2]=='sysfs':
74 return sline[1]
76 return None
78 def get_all_pci_names():
79 try:
80 sysfs_mnt = find_sysfs_mnt()
81 except IOError, (errno, strerr):
82 raise PciDeviceParseError(('Failed to locate sysfs mount: %s (%d)' %
83 (PROC_PCI_PATH, strerr, errno)))
85 pci_names = os.popen('ls ' + sysfs_mnt + SYSFS_PCI_DEVS_PATH).read().split()
87 return pci_names
89 def get_all_pci_devices():
90 pci_devs = []
91 for pci_name in get_all_pci_names():
92 pci_match = re.match(r"((?P<domain>[0-9a-fA-F]{1,4})[:,])?" + \
93 r"(?P<bus>[0-9a-fA-F]{1,2})[:,]" + \
94 r"(?P<slot>[0-9a-fA-F]{1,2})[.,]" + \
95 r"(?P<func>[0-7])$", pci_name)
96 if pci_match is None:
97 raise PciDeviceParseError(('Failed to parse pci device name: %s' %
98 pci_name))
99 pci_dev_info = pci_match.groupdict('0')
100 domain = parse_hex(pci_dev_info['domain'])
101 bus = parse_hex(pci_dev_info['bus'])
102 slot = parse_hex(pci_dev_info['slot'])
103 func = parse_hex(pci_dev_info['func'])
104 try:
105 pci_dev = PciDevice(domain, bus, slot, func)
106 except:
107 continue
108 pci_devs.append(pci_dev)
110 return pci_devs
112 def create_lspci_info():
113 global lspci_info
114 lspci_info = {}
116 # Execute 'lspci' command and parse the result.
117 # If the command does not exist, lspci_info will be kept blank ({}).
118 for paragraph in os.popen(LSPCI_CMD + ' -vmmD').read().split('\n\n'):
119 device_name = None
120 device_info = {}
121 for line in paragraph.split('\n'):
122 try:
123 (opt, value) = line.split(':\t')
124 if opt == 'Slot':
125 device_name = value
126 else:
127 device_info[opt] = value
128 except:
129 pass
130 if device_name is not None:
131 lspci_info[device_name] = device_info
133 class PciDeviceNotFoundError(Exception):
134 def __init__(self,domain,bus,slot,func):
135 self.domain = domain
136 self.bus = bus
137 self.slot = slot
138 self.func = func
139 self.name = "%04x:%02x:%02x.%01x"%(domain, bus, slot, func)
141 def __str__(self):
142 return ('PCI Device %s Not Found' % (self.name))
144 class PciDeviceParseError(Exception):
145 def __init__(self,msg):
146 self.message = msg
147 def __str__(self):
148 return 'Error Parsing PCI Device Info: '+self.message
150 class PciDevice:
151 def __init__(self, domain, bus, slot, func):
152 self.domain = domain
153 self.bus = bus
154 self.slot = slot
155 self.func = func
156 self.name = "%04x:%02x:%02x.%01x"%(domain, bus, slot, func)
157 self.irq = 0
158 self.iomem = []
159 self.ioports = []
160 self.driver = None
161 self.vendor = None
162 self.device = None
163 self.subvendor = None
164 self.subdevice = None
165 self.msix = 0
166 self.msix_iomem = []
167 self.revision = 0
168 self.classcode = None
169 self.vendorname = ""
170 self.devicename = ""
171 self.classname = ""
172 self.subvendorname = ""
173 self.subdevicename = ""
174 self.get_info_from_sysfs()
175 self.get_info_from_lspci()
177 def find_capability(self, type):
178 try:
179 sysfs_mnt = find_sysfs_mnt()
180 except IOError, (errno, strerr):
181 raise PciDeviceParseError(('Failed to locate sysfs mount: %s (%d)' %
182 (PROC_PCI_PATH, strerr, errno)))
184 if sysfs_mnt == None:
185 return False
186 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
187 self.name+SYSFS_PCI_DEV_CONFIG_PATH
188 try:
189 conf_file = open(path, 'rb')
190 conf_file.seek(PCI_STATUS_OFFSET)
191 status = ord(conf_file.read(1))
192 if status&PCI_STATUS_CAP_MASK:
193 conf_file.seek(PCI_CAP_OFFSET)
194 capa_pointer = ord(conf_file.read(1))
195 while capa_pointer:
196 conf_file.seek(capa_pointer)
197 capa_id = ord(conf_file.read(1))
198 capa_pointer = ord(conf_file.read(1))
199 if capa_id == type:
200 # get the type
201 message_cont_lo = ord(conf_file.read(1))
202 message_cont_hi = ord(conf_file.read(1))
203 self.msix=1
204 self.msix_entries = (message_cont_lo + \
205 (message_cont_hi << 8)) \
206 & MSIX_SIZE_MASK
207 t_off=conf_file.read(4)
208 p_off=conf_file.read(4)
209 self.table_offset=ord(t_off[0]) | (ord(t_off[1])<<8) | \
210 (ord(t_off[2])<<16)| \
211 (ord(t_off[3])<<24)
212 self.pba_offset=ord(p_off[0]) | (ord(p_off[1]) << 8)| \
213 (ord(p_off[2])<<16) | \
214 (ord(p_off[3])<<24)
215 self.table_index = self.table_offset & MSIX_BIR_MASK
216 self.table_offset = self.table_offset & ~MSIX_BIR_MASK
217 self.pba_index = self.pba_offset & MSIX_BIR_MASK
218 self.pba_offset = self.pba_offset & ~MSIX_BIR_MASK
219 break
220 except IOError, (errno, strerr):
221 raise PciDeviceParseError(('Failed to locate sysfs mount: %s (%d)' %
222 (PROC_PCI_PATH, strerr, errno)))
224 def remove_msix_iomem(self, index, start, size):
225 if (index == self.table_index):
226 table_start = start+self.table_offset
227 table_end = table_start + self.msix_entries * 16
228 table_start = table_start & PAGE_MASK
229 table_end = (table_end + PAGE_SIZE) & PAGE_MASK
230 self.msix_iomem.append((table_start, table_end-table_start))
231 if (index==self.pba_index):
232 pba_start = start + self.pba_offset
233 pba_end = pba_start + self.msix_entries/8
234 pba_start = pba_start & PAGE_MASK
235 pba_end = (pba_end + PAGE_SIZE) & PAGE_MASK
236 self.msix_iomem.append((pba_start, pba_end-pba_start))
238 def get_info_from_sysfs(self):
239 self.find_capability(0x11)
240 try:
241 sysfs_mnt = find_sysfs_mnt()
242 except IOError, (errno, strerr):
243 raise PciDeviceParseError(('Failed to locate sysfs mount: %s (%d)' %
244 (PROC_PCI_PATH, strerr, errno)))
246 if sysfs_mnt == None:
247 return False
249 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
250 self.name+SYSFS_PCI_DEV_RESOURCE_PATH
251 try:
252 resource_file = open(path,'r')
254 for i in range(PROC_PCI_NUM_RESOURCES):
255 line = resource_file.readline()
256 sline = line.split()
257 if len(sline)<3:
258 continue
260 start = int(sline[0],16)
261 end = int(sline[1],16)
262 flags = int(sline[2],16)
263 size = end-start+1
265 if start!=0:
266 if flags&PCI_BAR_IO:
267 self.ioports.append( (start,size) )
268 else:
269 self.iomem.append( (start,size) )
270 if (self.msix):
271 self.remove_msix_iomem(i, start, size)
275 except IOError, (errno, strerr):
276 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
277 (path, strerr, errno)))
279 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
280 self.name+SYSFS_PCI_DEV_IRQ_PATH
281 try:
282 self.irq = int(open(path,'r').readline())
283 except IOError, (errno, strerr):
284 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
285 (path, strerr, errno)))
287 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
288 self.name+SYSFS_PCI_DEV_DRIVER_DIR_PATH
289 try:
290 self.driver = os.path.basename(os.readlink(path))
291 except OSError, (errno, strerr):
292 self.driver = ""
294 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
295 self.name+SYSFS_PCI_DEV_VENDOR_PATH
296 try:
297 self.vendor = int(open(path,'r').readline(), 16)
298 except IOError, (errno, strerr):
299 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
300 (path, strerr, errno)))
302 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
303 self.name+SYSFS_PCI_DEV_DEVICE_PATH
304 try:
305 self.device = int(open(path,'r').readline(), 16)
306 except IOError, (errno, strerr):
307 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
308 (path, strerr, errno)))
310 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
311 self.name+SYSFS_PCI_DEV_SUBVENDOR_PATH
312 try:
313 self.subvendor = int(open(path,'r').readline(), 16)
314 except IOError, (errno, strerr):
315 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
316 (path, strerr, errno)))
318 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
319 self.name+SYSFS_PCI_DEV_SUBDEVICE_PATH
320 try:
321 self.subdevice = int(open(path,'r').readline(), 16)
322 except IOError, (errno, strerr):
323 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
324 (path, strerr, errno)))
326 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
327 self.name+SYSFS_PCI_DEV_CLASS_PATH
328 try:
329 self.classcode = int(open(path,'r').readline(), 16)
330 except IOError, (errno, strerr):
331 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
332 (path, strerr, errno)))
334 return True
336 def get_info_from_lspci(self):
337 """ Get information such as vendor name, device name, class name, etc.
338 Since we cannot obtain these data from sysfs, use 'lspci' command.
339 """
340 global lspci_info
342 if lspci_info is None:
343 create_lspci_info()
345 try:
346 device_info = lspci_info[self.name]
347 self.revision = int(device_info['Rev'], 16)
348 self.vendorname = device_info['Vendor']
349 self.devicename = device_info['Device']
350 self.classname = device_info['Class']
351 self.subvendorname = device_info['SVendor']
352 self.subdevicename = device_info['SDevice']
353 except KeyError:
354 pass
356 return True
358 def __str__(self):
359 str = "PCI Device %s\n" % (self.name)
360 for (start,size) in self.ioports:
361 str = str + "IO Port 0x%02x [size=%d]\n"%(start,size)
362 for (start,size) in self.iomem:
363 str = str + "IO Mem 0x%02x [size=%d]\n"%(start,size)
364 str = str + "IRQ %d\n"%(self.irq)
365 str = str + "Vendor ID 0x%04x\n"%(self.vendor)
366 str = str + "Device ID 0x%04x\n"%(self.device)
367 str = str + "Sybsystem Vendor ID 0x%04x\n"%(self.subvendor)
368 str = str + "Subsystem Device ID 0x%04x"%(self.subdevice)
369 return str
371 def main():
372 if len(sys.argv)<5:
373 print "Usage: %s <domain> <bus> <slot> <func>\n"
374 sys.exit(2)
376 dev = PciDevice(int(sys.argv[1],16), int(sys.argv[2],16),
377 int(sys.argv[3],16), int(sys.argv[4],16))
378 print str(dev)
380 if __name__=='__main__':
381 main()