ia64/xen-unstable

view tools/python/xen/util/pci.py @ 19776:9e36ef77f658

xend: pass-through: Common parse_pci_name()

Share some parsing code between different parts of xm.

This has the side-effect that the device specification for
hot-plug may now include the VSLOT and OPTS as per device
specifictions in the domain configuration file.

SEQ:BUS:DEV.FUNC[,OPT...]

e.g. 0000:00:01.00@6

Signed-off-by: Simon Horman <horms@verge.net.au>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Jun 17 07:34:59 2009 +0100 (2009-06-17)
parents 45ca3f3c3f98
children 60588f1f055f
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
13 import struct
14 import time
15 import threading
16 from xen.util import utils
17 from xen.xend import sxp
18 from xen.xend.XendConstants import AUTO_PHP_SLOT
20 PROC_PCI_PATH = '/proc/bus/pci/devices'
21 PROC_PCI_NUM_RESOURCES = 7
23 SYSFS_PCI_DEVS_PATH = '/bus/pci/devices'
24 SYSFS_PCI_DEV_RESOURCE_PATH = '/resource'
25 SYSFS_PCI_DEV_CONFIG_PATH = '/config'
26 SYSFS_PCI_DEV_IRQ_PATH = '/irq'
27 SYSFS_PCI_DEV_DRIVER_DIR_PATH = '/driver'
28 SYSFS_PCI_DEV_VENDOR_PATH = '/vendor'
29 SYSFS_PCI_DEV_DEVICE_PATH = '/device'
30 SYSFS_PCI_DEV_SUBVENDOR_PATH = '/subsystem_vendor'
31 SYSFS_PCI_DEV_SUBDEVICE_PATH = '/subsystem_device'
32 SYSFS_PCI_DEV_CLASS_PATH = '/class'
33 SYSFS_PCIBACK_PATH = '/bus/pci/drivers/pciback/'
35 LSPCI_CMD = 'lspci'
37 PCI_DEV_REG_EXPRESS_STR = r"[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}."+ \
38 r"[0-9a-fA-F]{1}"
40 DEV_TYPE_PCIe_ENDPOINT = 0
41 DEV_TYPE_PCIe_BRIDGE = 1
42 DEV_TYPE_PCI_BRIDGE = 2
43 DEV_TYPE_PCI = 3
45 PCI_VENDOR_ID = 0x0
46 PCI_STATUS = 0x6
47 PCI_CLASS_DEVICE = 0x0a
48 PCI_CLASS_BRIDGE_PCI = 0x0604
50 PCI_HEADER_TYPE = 0x0e
51 PCI_HEADER_TYPE_MASK = 0x7f
52 PCI_HEADER_TYPE_NORMAL = 0
53 PCI_HEADER_TYPE_BRIDGE = 1
54 PCI_HEADER_TYPE_CARDBUS = 2
56 PCI_CAPABILITY_LIST = 0x34
57 PCI_CB_BRIDGE_CONTROL = 0x3e
58 PCI_BRIDGE_CTL_BUS_RESET= 0x40
60 PCI_CAP_ID_EXP = 0x10
61 PCI_EXP_FLAGS = 0x2
62 PCI_EXP_FLAGS_TYPE = 0x00f0
63 PCI_EXP_TYPE_PCI_BRIDGE = 0x7
64 PCI_EXP_DEVCAP = 0x4
65 PCI_EXP_DEVCAP_FLR = (0x1 << 28)
66 PCI_EXP_DEVCTL = 0x8
67 PCI_EXP_DEVCTL_FLR = (0x1 << 15)
69 PCI_CAP_ID_PM = 0x01
70 PCI_PM_CTRL = 4
71 PCI_PM_CTRL_NO_SOFT_RESET = 0x0008
72 PCI_PM_CTRL_STATE_MASK = 0x0003
73 PCI_D3hot = 3
74 PCI_D0hot = 0
76 VENDOR_INTEL = 0x8086
77 PCI_CAP_ID_VENDOR_SPECIFIC_CAP = 0x09
78 PCI_CLASS_ID_USB = 0x0c03
79 PCI_USB_FLRCTRL = 0x4
81 PCI_CAP_ID_AF = 0x13
82 PCI_AF_CAPs = 0x3
83 PCI_AF_CAPs_TP_FLR = 0x3
84 PCI_AF_CTL = 0x4
85 PCI_AF_CTL_FLR = 0x1
87 PCI_BAR_0 = 0x10
88 PCI_BAR_5 = 0x24
89 PCI_BAR_SPACE = 0x01
90 PCI_BAR_IO = 0x01
91 PCI_BAR_IO_MASK = ~0x03
92 PCI_BAR_MEM = 0x00
93 PCI_BAR_MEM_MASK = ~0x0f
94 PCI_STATUS_CAP_MASK = 0x10
95 PCI_STATUS_OFFSET = 0x6
96 PCI_CAP_OFFSET = 0x34
97 MSIX_BIR_MASK = 0x7
98 MSIX_SIZE_MASK = 0x7ff
100 # Global variable to store information from lspci
101 lspci_info = None
102 lspci_info_lock = threading.RLock()
104 #Calculate PAGE_SHIFT: number of bits to shift an address to get the page number
105 PAGE_SIZE = resource.getpagesize()
106 PAGE_SHIFT = 0
107 t = PAGE_SIZE
108 while not (t&1):
109 t>>=1
110 PAGE_SHIFT+=1
112 PAGE_MASK=~(PAGE_SIZE - 1)
113 # Definitions from Linux: include/linux/pci.h
114 def PCI_DEVFN(slot, func):
115 return ((((slot) & 0x1f) << 3) | ((func) & 0x07))
117 def PCI_BDF(domain, bus, slot, func):
118 return (((domain & 0xffff) << 16) | ((bus & 0xff) << 8) |
119 PCI_DEVFN(slot, func))
121 def check_pci_opts(opts):
122 def f((k, v)):
123 if k not in ['msitranslate', 'power_mgmt'] or \
124 not v.lower() in ['0', '1', 'yes', 'no']:
125 raise PciDeviceParseError('Invalid pci option %s=%s: ' % (k, v))
127 map(f, opts)
129 def serialise_pci_opts(opts):
130 return ','.join(map(lambda x: '='.join(x), opts))
132 def split_pci_opts(opts):
133 return map(lambda x: x.split('='),
134 filter(lambda x: x != '', opts.split(',')))
136 def pci_opts_list_to_sxp(list):
137 return ['dev'] + map(lambda x: ['opts', x], list)
139 def pci_opts_list_from_sxp(dev):
140 return map(lambda x: sxp.children(x)[0], sxp.children(dev, 'opts'))
142 def parse_hex(val):
143 try:
144 if isinstance(val, types.StringTypes):
145 return int(val, 16)
146 else:
147 return val
148 except ValueError:
149 return None
151 def parse_pci_name_extended(pci_dev_str):
152 pci_match = re.match(r"((?P<domain>[0-9a-fA-F]{1,4})[:,])?" +
153 r"(?P<bus>[0-9a-fA-F]{1,2})[:,]" +
154 r"(?P<slot>[0-9a-fA-F]{1,2})[.,]" +
155 r"(?P<func>(\*|[0-7]))" +
156 r"(@(?P<vslot>[01]?[0-9a-fA-F]))?" +
157 r"(,(?P<opts>.*))?$", pci_dev_str)
159 if pci_match == None:
160 raise PciDeviceParseError("Failed to parse pci device: %s" %
161 pci_dev_str)
163 out = {}
164 pci_dev_info = pci_match.groupdict('')
165 if pci_dev_info['domain'] == '':
166 domain = 0
167 else:
168 domain = int(pci_dev_info['domain'], 16)
169 out['domain'] = "0x%04x" % domain
170 out['bus'] = "0x%02x" % int(pci_dev_info['bus'], 16)
171 out['slot'] = "0x%02x" % int(pci_dev_info['slot'], 16)
172 out['func'] = "0x%x" % int(pci_dev_info['func'], 16)
173 if pci_dev_info['vslot'] == '':
174 vslot = AUTO_PHP_SLOT
175 else:
176 vslot = int(pci_dev_info['vslot'], 16)
177 out['vslot'] = "0x%02x" % vslot
178 if pci_dev_info['opts'] != '':
179 out['opts'] = split_pci_opts(pci_dev_info['opts'])
180 check_pci_opts(out['opts'])
182 return out
184 def parse_pci_name(pci_name_string):
185 pci = parse_pci_name_extended(pci_name_string)
187 if int(pci['vslot'], 16) != AUTO_PHP_SLOT:
188 raise PciDeviceParseError(("Failed to parse pci device: %s: " +
189 "vslot provided where prohibited: %s") %
190 (pci_name_string, pci['vslot']))
191 if 'opts' in pci:
192 raise PciDeviceParseError(("Failed to parse pci device: %s: " +
193 "options provided where prohibited: %s") %
194 (pci_name_string, pci['opts']))
196 return pci
198 def __pci_dict_to_fmt_str(fmt, dev):
199 return fmt % (int(dev['domain'], 16), int(dev['bus'], 16),
200 int(dev['slot'], 16), int(dev['func'], 16))
202 def pci_dict_to_bdf_str(dev):
203 return __pci_dict_to_fmt_str('%04x:%02x:%02x.%01x', dev)
205 def pci_dict_to_xc_str(dev):
206 return __pci_dict_to_fmt_str('0x%x, 0x%x, 0x%x, 0x%x', dev)
208 def extract_the_exact_pci_names(pci_names):
209 result = []
211 if isinstance(pci_names, types.StringTypes):
212 pci_names = pci_names.split()
213 elif isinstance(pci_names, types.ListType):
214 pci_names = re.findall(PCI_DEV_REG_EXPRESS_STR, '%s' % pci_names)
215 else:
216 raise PciDeviceParseError('Invalid argument: %s' % pci_names)
218 for pci in pci_names:
219 # The length of DDDD:bb:dd.f is 12.
220 if len(pci) != 12:
221 continue
222 if re.match(PCI_DEV_REG_EXPRESS_STR, pci) is None:
223 continue
224 result = result + [pci]
225 return result
227 def find_sysfs_mnt():
228 try:
229 return utils.find_sysfs_mount()
230 except IOError, (errno, strerr):
231 raise PciDeviceParseError(('Failed to locate sysfs mount: %s: %s (%d)'%
232 (PROC_PCI_PATH, strerr, errno)))
233 return None
235 def get_all_pci_names():
236 sysfs_mnt = find_sysfs_mnt()
237 pci_names = os.popen('ls ' + sysfs_mnt + SYSFS_PCI_DEVS_PATH).read().split()
238 return pci_names
240 def get_all_pci_devices():
241 return map(PciDevice, map(parse_pci_name, get_all_pci_names()))
243 def _create_lspci_info():
244 """Execute 'lspci' command and parse the result.
245 If the command does not exist, lspci_info will be kept blank ({}).
247 Expects to be protected by lspci_info_lock.
248 """
249 global lspci_info
251 lspci_info = {}
253 for paragraph in os.popen(LSPCI_CMD + ' -vmm').read().split('\n\n'):
254 device_name = None
255 device_info = {}
256 # FIXME: workaround for pciutils without the -mm option.
257 # see: git://git.kernel.org/pub/scm/utils/pciutils/pciutils.git
258 # commit: 3fd6b4d2e2fda814047664ffc67448ac782a8089
259 first_device = True
260 for line in paragraph.split('\n'):
261 try:
262 (opt, value) = line.split(':\t')
263 if opt == 'Slot' or (opt == 'Device' and first_device):
264 device_name = pci_dict_to_bdf_str(parse_pci_name(value))
265 first_device = False
266 else:
267 device_info[opt] = value
268 except:
269 pass
270 if device_name is not None:
271 lspci_info[device_name] = device_info
273 def create_lspci_info():
274 global lspci_info_lock
275 lspci_info_lock.acquire()
276 try:
277 _create_lspci_info()
278 finally:
279 lspci_info_lock.release()
281 def save_pci_conf_space(devs_string):
282 pci_list = []
283 cfg_list = []
284 sysfs_mnt = find_sysfs_mnt()
285 for pci_str in devs_string:
286 pci_path = sysfs_mnt + SYSFS_PCI_DEVS_PATH + '/' + pci_str + \
287 SYSFS_PCI_DEV_CONFIG_PATH
288 fd = os.open(pci_path, os.O_RDONLY)
289 configs = []
290 for i in range(0, 256, 4):
291 configs = configs + [os.read(fd,4)]
292 os.close(fd)
293 pci_list = pci_list + [pci_path]
294 cfg_list = cfg_list + [configs]
295 return (pci_list, cfg_list)
297 def restore_pci_conf_space(pci_cfg_list):
298 pci_list = pci_cfg_list[0]
299 cfg_list = pci_cfg_list[1]
300 for i in range(0, len(pci_list)):
301 pci_path = pci_list[i]
302 configs = cfg_list[i]
303 fd = os.open(pci_path, os.O_WRONLY)
304 for dw in configs:
305 os.write(fd, dw)
306 os.close(fd)
308 def find_all_devices_owned_by_pciback():
309 sysfs_mnt = find_sysfs_mnt()
310 pciback_path = sysfs_mnt + SYSFS_PCIBACK_PATH
311 pci_names = os.popen('ls ' + pciback_path).read()
312 pci_list = extract_the_exact_pci_names(pci_names)
313 dev_list = []
314 for pci in pci_list:
315 dev = PciDevice(parse_pci_name(pci))
316 dev_list = dev_list + [dev]
317 return dev_list
319 def transform_list(target, src):
320 ''' src: its element is pci string (Format: xxxx:xx:xx.x).
321 target: its element is pci string, or a list of pci string.
323 If all the elements in src are in target, we remove them from target
324 and add src into target; otherwise, we remove from target all the
325 elements that also appear in src.
326 '''
327 result = []
328 target_contains_src = True
329 for e in src:
330 if not e in target:
331 target_contains_src = False
332 break
334 if target_contains_src:
335 result = result + [src]
336 for e in target:
337 if not e in src:
338 result = result + [e]
339 return result
341 def check_FLR_capability(dev_list):
342 if len(dev_list) == 0:
343 return []
345 pci_list = []
346 pci_dev_dict = {}
347 for dev in dev_list:
348 pci_list = pci_list + [dev.name]
349 pci_dev_dict[dev.name] = dev
351 while True:
352 need_transform = False
353 for pci in pci_list:
354 if isinstance(pci, types.StringTypes):
355 dev = pci_dev_dict[pci]
356 if dev.bus == 0:
357 continue
358 if dev.dev_type == DEV_TYPE_PCIe_ENDPOINT and not dev.pcie_flr:
359 coassigned_pci_list = dev.find_all_the_multi_functions()
360 need_transform = True
361 elif dev.dev_type == DEV_TYPE_PCI and not dev.pci_af_flr:
362 coassigned_pci_list = dev.find_coassigned_pci_devices(True)
363 del coassigned_pci_list[0]
364 need_transform = True
366 if need_transform:
367 pci_list = transform_list(pci_list, coassigned_pci_list)
368 if not need_transform:
369 break
371 if len(pci_list) == 0:
372 return []
374 for i in range(0, len(pci_list)):
375 if isinstance(pci_list[i], types.StringTypes):
376 pci_list[i] = [pci_list[i]]
378 # Now every element in pci_list is a list of pci string.
380 result = []
381 for pci_names in pci_list:
382 devs = []
383 for pci in pci_names:
384 devs = devs + [pci_dev_dict[pci]]
385 result = result + [devs]
386 return result
388 def check_mmio_bar(devs_list):
389 result = []
391 for dev_list in devs_list:
392 non_aligned_bar_found = False
393 for dev in dev_list:
394 if dev.has_non_page_aligned_bar:
395 non_aligned_bar_found = True
396 break
397 if not non_aligned_bar_found:
398 result = result + [dev_list]
400 return result
402 class PciDeviceParseError(Exception):
403 def __init__(self,msg):
404 self.message = msg
405 def __str__(self):
406 return 'Error Parsing PCI Device Info: '+self.message
408 class PciDeviceAssignmentError(Exception):
409 def __init__(self,msg):
410 self.message = msg
411 def __str__(self):
412 return 'pci: improper device assignment specified: ' + \
413 self.message
415 class PciDeviceVslotMissing(Exception):
416 def __init__(self,msg):
417 self.message = msg
418 def __str__(self):
419 return 'pci: no vslot: ' + self.message
421 class PciDevice:
422 def __init__(self, dev):
423 self.domain = int(dev['domain'], 16)
424 self.bus = int(dev['bus'], 16)
425 self.slot = int(dev['slot'], 16)
426 self.func = int(dev['func'], 16)
427 self.name = pci_dict_to_bdf_str(dev)
428 self.cfg_space_path = find_sysfs_mnt()+SYSFS_PCI_DEVS_PATH+'/'+ \
429 self.name + SYSFS_PCI_DEV_CONFIG_PATH
430 self.irq = 0
431 self.iomem = []
432 self.ioports = []
433 self.driver = None
434 self.vendor = None
435 self.device = None
436 self.subvendor = None
437 self.subdevice = None
438 self.msix = 0
439 self.msix_iomem = []
440 self.revision = 0
441 self.classcode = None
442 self.vendorname = ""
443 self.devicename = ""
444 self.classname = ""
445 self.subvendorname = ""
446 self.subdevicename = ""
447 self.dev_type = None
448 self.has_non_page_aligned_bar = False
449 self.pcie_flr = False
450 self.pci_af_flr = False
451 self.detect_dev_info()
452 self.get_info_from_sysfs()
453 self.get_info_from_lspci()
455 def find_parent(self):
456 # i.e., /sys/bus/pci/devices/0000:00:19.0 or
457 # /sys/bus/pci/devices/0000:03:04.0
458 path = find_sysfs_mnt()+SYSFS_PCI_DEVS_PATH+'/'+ self.name
459 # i.e., ../../../devices/pci0000:00/0000:00:19.0
460 # ../../../devices/pci0000:00/0000:00:02.0/0000:01:00.2/0000:03:04.0
461 try:
462 target = os.readlink(path)
463 lst = target.split('/')
464 parent = lst[len(lst)-2]
465 if parent[0:3] == 'pci':
466 # We have reached the upmost one.
467 return None
468 else:
469 dev = {}
470 lst = parent.split(':')
471 dev['domain'] = int(lst[0], 16)
472 dev['bus'] = int(lst[1], 16)
473 lst = lst[2]
474 lst = lst.split('.')
475 dev['slot'] = int(lst[0], 16)
476 dev['func'] = int(lst[1], 16)
477 return dev
478 except OSError, (errno, strerr):
479 raise PciDeviceParseError('Can not locate the parent of %s',
480 self.name)
482 def find_the_uppermost_pci_bridge(self):
483 # Find the uppermost PCI/PCI-X bridge
484 dev = self.find_parent()
485 if dev is None:
486 return None
487 dev = dev_parent = PciDevice(dev)
488 while dev_parent.dev_type != DEV_TYPE_PCIe_BRIDGE:
489 parent = dev_parent.find_parent()
490 if parent is None:
491 break
492 dev = dev_parent
493 dev_parent = PciDevice(parent)
494 return dev
496 def find_all_devices_behind_the_bridge(self, ignore_bridge):
497 sysfs_mnt = find_sysfs_mnt()
498 self_path = sysfs_mnt + SYSFS_PCI_DEVS_PATH + '/' + self.name
499 pci_names = os.popen('ls ' + self_path).read()
500 dev_list = extract_the_exact_pci_names(pci_names)
502 list = [self.name]
503 for pci_str in dev_list:
504 dev = PciDevice(parse_pci_name(pci_str))
505 if dev.dev_type == DEV_TYPE_PCI_BRIDGE or \
506 dev.dev_type == DEV_TYPE_PCIe_BRIDGE:
507 sub_list_including_self = \
508 dev.find_all_devices_behind_the_bridge(ignore_bridge)
509 if ignore_bridge:
510 del sub_list_including_self[0]
511 list = list + [sub_list_including_self]
512 else:
513 list = list + [dev.name]
514 return list
516 def find_coassigned_pci_devices(self, ignore_bridge = True):
517 ''' Here'self' is a PCI device, we need find the uppermost PCI/PCI-X
518 bridge, and all devices behind it must be co-assigned to the same
519 guest.
521 Parameter:
522 [ignore_bridge]: if set, the returned result doesn't include
523 any bridge behind the uppermost PCI/PCI-X bridge.
525 Note: The first element of the return value is the uppermost
526 PCI/PCI-X bridge. If the caller doesn't need the first
527 element, the caller itself can remove it explicitly.
528 '''
529 dev = self.find_the_uppermost_pci_bridge()
531 # The 'self' device is on bus0.
532 if dev is None:
533 return [self.name]
535 dev_list = dev.find_all_devices_behind_the_bridge(ignore_bridge)
536 dev_list = extract_the_exact_pci_names(dev_list)
537 return dev_list
539 def do_secondary_bus_reset(self, target_bus, devs):
540 # Save the config spaces of all the devices behind the bus.
541 (pci_list, cfg_list) = save_pci_conf_space(devs)
543 #Do the Secondary Bus Reset
544 sysfs_mnt = find_sysfs_mnt()
545 parent_path = sysfs_mnt + SYSFS_PCI_DEVS_PATH + '/' + \
546 target_bus + SYSFS_PCI_DEV_CONFIG_PATH
547 fd = os.open(parent_path, os.O_RDWR)
548 os.lseek(fd, PCI_CB_BRIDGE_CONTROL, 0)
549 br_cntl = (struct.unpack('H', os.read(fd, 2)))[0]
550 # Assert Secondary Bus Reset
551 os.lseek(fd, PCI_CB_BRIDGE_CONTROL, 0)
552 br_cntl |= PCI_BRIDGE_CTL_BUS_RESET
553 os.write(fd, struct.pack('H', br_cntl))
554 time.sleep(0.100)
555 # De-assert Secondary Bus Reset
556 os.lseek(fd, PCI_CB_BRIDGE_CONTROL, 0)
557 br_cntl &= ~PCI_BRIDGE_CTL_BUS_RESET
558 os.write(fd, struct.pack('H', br_cntl))
559 time.sleep(0.100)
560 os.close(fd)
562 # Restore the config spaces
563 restore_pci_conf_space((pci_list, cfg_list))
565 def do_Dstate_transition(self):
566 pos = self.find_cap_offset(PCI_CAP_ID_PM)
567 if pos == 0:
568 return False
570 # No_Soft_Reset - When set 1, this bit indicates that
571 # devices transitioning from D3hot to D0 because of
572 # PowerState commands do not perform an internal reset.
573 pm_ctl = self.pci_conf_read32(pos + PCI_PM_CTRL)
574 if (pm_ctl & PCI_PM_CTRL_NO_SOFT_RESET) == 1:
575 return False
577 (pci_list, cfg_list) = save_pci_conf_space([self.name])
579 # Enter D3hot
580 pm_ctl &= ~PCI_PM_CTRL_STATE_MASK
581 pm_ctl |= PCI_D3hot
582 self.pci_conf_write32(pos + PCI_PM_CTRL, pm_ctl)
583 time.sleep(0.010)
585 # From D3hot to D0
586 pm_ctl &= ~PCI_PM_CTRL_STATE_MASK
587 pm_ctl |= PCI_D0hot
588 self.pci_conf_write32(pos + PCI_PM_CTRL, pm_ctl)
589 time.sleep(0.010)
591 restore_pci_conf_space((pci_list, cfg_list))
592 return True
594 def do_vendor_specific_FLR_method(self):
595 pos = self.find_cap_offset(PCI_CAP_ID_VENDOR_SPECIFIC_CAP)
596 if pos == 0:
597 return
599 vendor_id = self.pci_conf_read16(PCI_VENDOR_ID)
600 if vendor_id != VENDOR_INTEL:
601 return
603 class_id = self.pci_conf_read16(PCI_CLASS_DEVICE)
604 if class_id != PCI_CLASS_ID_USB:
605 return
607 (pci_list, cfg_list) = save_pci_conf_space([self.name])
609 self.pci_conf_write8(pos + PCI_USB_FLRCTRL, 1)
610 time.sleep(0.100)
612 restore_pci_conf_space((pci_list, cfg_list))
614 def do_FLR_for_integrated_device(self):
615 if not self.do_Dstate_transition():
616 self.do_vendor_specific_FLR_method()
618 def find_all_the_multi_functions(self):
619 sysfs_mnt = find_sysfs_mnt()
620 parent = pci_dict_to_bdf_str(self.find_parent())
621 pci_names = os.popen('ls ' + sysfs_mnt + SYSFS_PCI_DEVS_PATH + '/' + \
622 parent + '/').read()
623 funcs = extract_the_exact_pci_names(pci_names)
624 return funcs
626 def find_coassigned_devices(self):
627 if self.dev_type == DEV_TYPE_PCIe_ENDPOINT and not self.pcie_flr:
628 return self.find_all_the_multi_functions()
629 elif self.dev_type == DEV_TYPE_PCI and not self.pci_af_flr:
630 coassigned_pci_list = self.find_coassigned_pci_devices(True)
631 if len(coassigned_pci_list) > 1:
632 del coassigned_pci_list[0]
633 return coassigned_pci_list
634 else:
635 return [self.name]
637 def find_cap_offset(self, cap):
638 path = find_sysfs_mnt()+SYSFS_PCI_DEVS_PATH+'/'+ \
639 self.name+SYSFS_PCI_DEV_CONFIG_PATH
641 pos = PCI_CAPABILITY_LIST
643 try:
644 fd = os.open(path, os.O_RDONLY)
645 os.lseek(fd, PCI_STATUS, 0)
646 status = struct.unpack('H', os.read(fd, 2))[0]
647 if (status & 0x10) == 0:
648 # The device doesn't support PCI_STATUS_CAP_LIST
649 return 0
651 max_cap = 48
652 while max_cap > 0:
653 os.lseek(fd, pos, 0)
654 pos = ord(os.read(fd, 1))
655 if pos < 0x40:
656 pos = 0
657 break;
658 os.lseek(fd, pos + 0, 0)
659 id = ord(os.read(fd, 1))
660 if id == 0xff:
661 pos = 0
662 break;
664 # Found the capability
665 if id == cap:
666 break;
668 # Test the next one
669 pos = pos + 1
670 max_cap = max_cap - 1;
672 os.close(fd)
673 except OSError, (errno, strerr):
674 raise PciDeviceParseError(('Error when accessing sysfs: %s (%d)' %
675 (strerr, errno)))
676 return pos
678 def pci_conf_read8(self, pos):
679 fd = os.open(self.cfg_space_path, os.O_RDONLY)
680 os.lseek(fd, pos, 0)
681 str = os.read(fd, 1)
682 os.close(fd)
683 val = struct.unpack('B', str)[0]
684 return val
686 def pci_conf_read16(self, pos):
687 fd = os.open(self.cfg_space_path, os.O_RDONLY)
688 os.lseek(fd, pos, 0)
689 str = os.read(fd, 2)
690 os.close(fd)
691 val = struct.unpack('H', str)[0]
692 return val
694 def pci_conf_read32(self, pos):
695 fd = os.open(self.cfg_space_path, os.O_RDONLY)
696 os.lseek(fd, pos, 0)
697 str = os.read(fd, 4)
698 os.close(fd)
699 val = struct.unpack('I', str)[0]
700 return val
702 def pci_conf_write8(self, pos, val):
703 str = struct.pack('B', val)
704 fd = os.open(self.cfg_space_path, os.O_WRONLY)
705 os.lseek(fd, pos, 0)
706 os.write(fd, str)
707 os.close(fd)
709 def pci_conf_write16(self, pos, val):
710 str = struct.pack('H', val)
711 fd = os.open(self.cfg_space_path, os.O_WRONLY)
712 os.lseek(fd, pos, 0)
713 os.write(fd, str)
714 os.close(fd)
716 def pci_conf_write32(self, pos, val):
717 str = struct.pack('I', val)
718 fd = os.open(self.cfg_space_path, os.O_WRONLY)
719 os.lseek(fd, pos, 0)
720 os.write(fd, str)
721 os.close(fd)
723 def detect_dev_info(self):
724 class_dev = self.pci_conf_read16(PCI_CLASS_DEVICE)
725 pos = self.find_cap_offset(PCI_CAP_ID_EXP)
726 if class_dev == PCI_CLASS_BRIDGE_PCI:
727 if pos == 0:
728 self.dev_type = DEV_TYPE_PCI_BRIDGE
729 else:
730 creg = self.pci_conf_read16(pos + PCI_EXP_FLAGS)
731 if ((creg & PCI_EXP_FLAGS_TYPE) >> 4) == \
732 PCI_EXP_TYPE_PCI_BRIDGE:
733 self.dev_type = DEV_TYPE_PCI_BRIDGE
734 else:
735 self.dev_type = DEV_TYPE_PCIe_BRIDGE
736 else:
737 if pos != 0:
738 self.dev_type = DEV_TYPE_PCIe_ENDPOINT
739 else:
740 self.dev_type = DEV_TYPE_PCI
742 # Force 0000:00:00.0 to be DEV_TYPE_PCIe_BRIDGE
743 if self.name == '0000:00:00.0':
744 self.dev_type = DEV_TYPE_PCIe_BRIDGE
746 if (self.dev_type == DEV_TYPE_PCI_BRIDGE) or \
747 (self.dev_type == DEV_TYPE_PCIe_BRIDGE):
748 return
750 # Try to findthe PCIe FLR capability
751 if self.dev_type == DEV_TYPE_PCIe_ENDPOINT:
752 dev_cap = self.pci_conf_read32(pos + PCI_EXP_DEVCAP)
753 if dev_cap & PCI_EXP_DEVCAP_FLR:
754 self.pcie_flr = True
755 elif self.dev_type == DEV_TYPE_PCI:
756 # Try to find the "PCI Advanced Capabilities"
757 pos = self.find_cap_offset(PCI_CAP_ID_AF)
758 if pos != 0:
759 af_cap = self.pci_conf_read8(pos + PCI_AF_CAPs)
760 if (af_cap & PCI_AF_CAPs_TP_FLR) == PCI_AF_CAPs_TP_FLR:
761 self.pci_af_flr = True
763 bar_addr = PCI_BAR_0
764 while bar_addr <= PCI_BAR_5:
765 bar = self.pci_conf_read32(bar_addr)
766 if (bar & PCI_BAR_SPACE) == PCI_BAR_MEM:
767 bar = bar & PCI_BAR_MEM_MASK
768 bar = bar & ~PAGE_MASK
769 if bar != 0:
770 self.has_non_page_aligned_bar = True
771 break
772 bar_addr = bar_addr + 4
774 def devs_check_driver(self, devs):
775 if len(devs) == 0:
776 return
777 for pci_dev in devs:
778 dev = PciDevice(parse_pci_name(pci_dev))
779 if dev.driver == 'pciback':
780 continue
781 err_msg = 'pci: %s must be co-assigned to the same guest with %s' + \
782 ', but it is not owned by pciback.'
783 raise PciDeviceAssignmentError(err_msg % (pci_dev, self.name))
785 def do_FLR(self):
786 """ Perform FLR (Functional Level Reset) for the device.
787 """
788 if self.dev_type == DEV_TYPE_PCIe_ENDPOINT:
789 # If PCIe device supports FLR, we use it.
790 if self.pcie_flr:
791 (pci_list, cfg_list) = save_pci_conf_space([self.name])
792 pos = self.find_cap_offset(PCI_CAP_ID_EXP)
793 self.pci_conf_write32(pos + PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_FLR)
794 # We must sleep at least 100ms for the completion of FLR
795 time.sleep(0.100)
796 restore_pci_conf_space((pci_list, cfg_list))
797 else:
798 if self.bus == 0:
799 self.do_FLR_for_integrated_device()
800 else:
801 funcs = self.find_all_the_multi_functions()
802 self.devs_check_driver(funcs)
804 parent = pci_dict_to_bdf_str(self.find_parent())
806 # Do Secondary Bus Reset.
807 self.do_secondary_bus_reset(parent, funcs)
808 # PCI devices
809 else:
810 # For PCI device on host bus, we test "PCI Advanced Capabilities".
811 if self.bus == 0 and self.pci_af_flr:
812 (pci_list, cfg_list) = save_pci_conf_space([self.name])
813 # We use Advanced Capability to do FLR.
814 pos = self.find_cap_offset(PCI_CAP_ID_AF)
815 self.pci_conf_write8(pos + PCI_AF_CTL, PCI_AF_CTL_FLR)
816 time.sleep(0.100)
817 restore_pci_conf_space((pci_list, cfg_list))
818 else:
819 if self.bus == 0:
820 self.do_FLR_for_integrated_device()
821 else:
822 devs = self.find_coassigned_pci_devices(False)
823 # Remove the element 0 which is a bridge
824 target_bus = devs[0]
825 del devs[0]
826 self.devs_check_driver(devs)
828 # Do Secondary Bus Reset.
829 self.do_secondary_bus_reset(target_bus, devs)
831 def find_capability(self, type):
832 sysfs_mnt = find_sysfs_mnt()
833 if sysfs_mnt == None:
834 return False
835 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
836 self.name+SYSFS_PCI_DEV_CONFIG_PATH
837 try:
838 conf_file = open(path, 'rb')
839 conf_file.seek(PCI_HEADER_TYPE)
840 header_type = ord(conf_file.read(1)) & PCI_HEADER_TYPE_MASK
841 if header_type == PCI_HEADER_TYPE_CARDBUS:
842 return
843 conf_file.seek(PCI_STATUS_OFFSET)
844 status = ord(conf_file.read(1))
845 if status&PCI_STATUS_CAP_MASK:
846 conf_file.seek(PCI_CAP_OFFSET)
847 capa_pointer = ord(conf_file.read(1))
848 capa_count = 0
849 while capa_pointer:
850 if capa_pointer < 0x40:
851 raise PciDeviceParseError(
852 ('Broken capability chain: %s' % self.name))
853 capa_count += 1
854 if capa_count > 96:
855 raise PciDeviceParseError(
856 ('Looped capability chain: %s' % self.name))
857 conf_file.seek(capa_pointer)
858 capa_id = ord(conf_file.read(1))
859 capa_pointer = ord(conf_file.read(1))
860 if capa_id == type:
861 # get the type
862 message_cont_lo = ord(conf_file.read(1))
863 message_cont_hi = ord(conf_file.read(1))
864 self.msix=1
865 self.msix_entries = (message_cont_lo + \
866 (message_cont_hi << 8)) \
867 & MSIX_SIZE_MASK
868 t_off=conf_file.read(4)
869 p_off=conf_file.read(4)
870 self.table_offset=ord(t_off[0]) | (ord(t_off[1])<<8) | \
871 (ord(t_off[2])<<16)| \
872 (ord(t_off[3])<<24)
873 self.pba_offset=ord(p_off[0]) | (ord(p_off[1]) << 8)| \
874 (ord(p_off[2])<<16) | \
875 (ord(p_off[3])<<24)
876 self.table_index = self.table_offset & MSIX_BIR_MASK
877 self.table_offset = self.table_offset & ~MSIX_BIR_MASK
878 self.pba_index = self.pba_offset & MSIX_BIR_MASK
879 self.pba_offset = self.pba_offset & ~MSIX_BIR_MASK
880 break
881 except IOError, (errno, strerr):
882 raise PciDeviceParseError(('Failed to locate sysfs mount: %s: %s (%d)' %
883 (PROC_PCI_PATH, strerr, errno)))
885 def remove_msix_iomem(self, index, start, size):
886 if (index == self.table_index):
887 table_start = start+self.table_offset
888 table_end = table_start + self.msix_entries * 16
889 table_start = table_start & PAGE_MASK
890 table_end = (table_end + PAGE_SIZE) & PAGE_MASK
891 self.msix_iomem.append((table_start, table_end-table_start))
892 if (index==self.pba_index):
893 pba_start = start + self.pba_offset
894 pba_end = pba_start + self.msix_entries/8
895 pba_start = pba_start & PAGE_MASK
896 pba_end = (pba_end + PAGE_SIZE) & PAGE_MASK
897 self.msix_iomem.append((pba_start, pba_end-pba_start))
899 def get_info_from_sysfs(self):
900 self.find_capability(0x11)
901 sysfs_mnt = find_sysfs_mnt()
902 if sysfs_mnt == None:
903 return False
905 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
906 self.name+SYSFS_PCI_DEV_RESOURCE_PATH
907 try:
908 resource_file = open(path,'r')
910 for i in range(PROC_PCI_NUM_RESOURCES):
911 line = resource_file.readline()
912 sline = line.split()
913 if len(sline)<3:
914 continue
916 start = int(sline[0],16)
917 end = int(sline[1],16)
918 flags = int(sline[2],16)
919 size = end-start+1
921 if start!=0:
922 if flags&PCI_BAR_IO:
923 self.ioports.append( (start,size) )
924 else:
925 self.iomem.append( (start,size) )
926 if (self.msix):
927 self.remove_msix_iomem(i, start, size)
931 except IOError, (errno, strerr):
932 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
933 (path, strerr, errno)))
935 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
936 self.name+SYSFS_PCI_DEV_IRQ_PATH
937 try:
938 self.irq = int(open(path,'r').readline())
939 except IOError, (errno, strerr):
940 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
941 (path, strerr, errno)))
943 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
944 self.name+SYSFS_PCI_DEV_DRIVER_DIR_PATH
945 try:
946 self.driver = os.path.basename(os.readlink(path))
947 except OSError, (errno, strerr):
948 self.driver = ""
950 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
951 self.name+SYSFS_PCI_DEV_VENDOR_PATH
952 try:
953 self.vendor = int(open(path,'r').readline(), 16)
954 except IOError, (errno, strerr):
955 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
956 (path, strerr, errno)))
958 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
959 self.name+SYSFS_PCI_DEV_DEVICE_PATH
960 try:
961 self.device = int(open(path,'r').readline(), 16)
962 except IOError, (errno, strerr):
963 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
964 (path, strerr, errno)))
966 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
967 self.name+SYSFS_PCI_DEV_SUBVENDOR_PATH
968 try:
969 self.subvendor = int(open(path,'r').readline(), 16)
970 except IOError, (errno, strerr):
971 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
972 (path, strerr, errno)))
974 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
975 self.name+SYSFS_PCI_DEV_SUBDEVICE_PATH
976 try:
977 self.subdevice = int(open(path,'r').readline(), 16)
978 except IOError, (errno, strerr):
979 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
980 (path, strerr, errno)))
982 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
983 self.name+SYSFS_PCI_DEV_CLASS_PATH
984 try:
985 self.classcode = int(open(path,'r').readline(), 16)
986 except IOError, (errno, strerr):
987 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
988 (path, strerr, errno)))
990 return True
992 def get_info_from_lspci(self):
993 """ Get information such as vendor name, device name, class name, etc.
994 Since we cannot obtain these data from sysfs, use 'lspci' command.
995 """
996 global lspci_info
997 global lspci_info_lock
999 lspci_info_lock.acquire()
1000 try:
1001 if lspci_info is None:
1002 _create_lspci_info()
1004 device_info = lspci_info.get(self.name)
1005 if device_info:
1006 try:
1007 self.revision = int(device_info.get('Rev', '0'), 16)
1008 except ValueError:
1009 pass
1010 self.vendorname = device_info.get('Vendor', '')
1011 self.devicename = device_info.get('Device', '')
1012 self.classname = device_info.get('Class', '')
1013 self.subvendorname = device_info.get('SVendor', '')
1014 self.subdevicename = device_info.get('SDevice', '')
1015 return True
1016 finally:
1017 lspci_info_lock.release()
1019 def __str__(self):
1020 str = "PCI Device %s\n" % (self.name)
1021 for (start,size) in self.ioports:
1022 str = str + "IO Port 0x%02x [size=%d]\n"%(start,size)
1023 for (start,size) in self.iomem:
1024 str = str + "IO Mem 0x%02x [size=%d]\n"%(start,size)
1025 str = str + "IRQ %d\n"%(self.irq)
1026 str = str + "Vendor ID 0x%04x\n"%(self.vendor)
1027 str = str + "Device ID 0x%04x\n"%(self.device)
1028 str = str + "Sybsystem Vendor ID 0x%04x\n"%(self.subvendor)
1029 str = str + "Subsystem Device ID 0x%04x"%(self.subdevice)
1030 return str
1032 def main():
1033 if len(sys.argv)<5:
1034 print "Usage: %s <domain> <bus> <slot> <func>\n" % sys.argv[0]
1035 sys.exit(2)
1037 dev = PciDevice(int(sys.argv[1],16), int(sys.argv[2],16),
1038 int(sys.argv[3],16), int(sys.argv[4],16))
1039 print str(dev)
1041 if __name__=='__main__':
1042 main()