ia64/xen-unstable

view tools/python/xen/util/pci.py @ 19789:cf6d3211ec59

xend: pci: find_parent: should return string rather than int

Using changeset 19783: 61ec78692b13, device assignment can't work:
e.g., in find_the_uppermost_pci_bridge(),
parent = dev_parent.find_parent()
...
dev_parent = PciDevice(parent),
we can see parent['domain'] is int and in PciDevice's __init__,
int(dev['domain'], 16) would fail:
TypeError: int() can't convert non-string with explicit base

Signed-off-by: Dexuan Cui <dexuan.cui@intel.com>
author Keir Fraser <keir.fraser@citrix.com>
date Thu Jun 18 10:18:10 2009 +0100 (2009-06-18)
parents bc7715954087
children
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 uuid
18 from xen.xend import sxp
19 from xen.xend.XendConstants import AUTO_PHP_SLOT
20 from xen.xend.XendSXPDev import dev_dict_to_sxp
22 PROC_PCI_PATH = '/proc/bus/pci/devices'
23 PROC_PCI_NUM_RESOURCES = 7
25 SYSFS_PCI_DEVS_PATH = '/bus/pci/devices'
26 SYSFS_PCI_DEV_RESOURCE_PATH = '/resource'
27 SYSFS_PCI_DEV_CONFIG_PATH = '/config'
28 SYSFS_PCI_DEV_IRQ_PATH = '/irq'
29 SYSFS_PCI_DEV_DRIVER_DIR_PATH = '/driver'
30 SYSFS_PCI_DEV_VENDOR_PATH = '/vendor'
31 SYSFS_PCI_DEV_DEVICE_PATH = '/device'
32 SYSFS_PCI_DEV_SUBVENDOR_PATH = '/subsystem_vendor'
33 SYSFS_PCI_DEV_SUBDEVICE_PATH = '/subsystem_device'
34 SYSFS_PCI_DEV_CLASS_PATH = '/class'
35 SYSFS_PCIBACK_PATH = '/bus/pci/drivers/pciback/'
37 LSPCI_CMD = 'lspci'
39 PCI_DEV_REG_EXPRESS_STR = r"[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}."+ \
40 r"[0-9a-fA-F]{1}"
42 DEV_TYPE_PCIe_ENDPOINT = 0
43 DEV_TYPE_PCIe_BRIDGE = 1
44 DEV_TYPE_PCI_BRIDGE = 2
45 DEV_TYPE_PCI = 3
47 PCI_VENDOR_ID = 0x0
48 PCI_STATUS = 0x6
49 PCI_CLASS_DEVICE = 0x0a
50 PCI_CLASS_BRIDGE_PCI = 0x0604
52 PCI_HEADER_TYPE = 0x0e
53 PCI_HEADER_TYPE_MASK = 0x7f
54 PCI_HEADER_TYPE_NORMAL = 0
55 PCI_HEADER_TYPE_BRIDGE = 1
56 PCI_HEADER_TYPE_CARDBUS = 2
58 PCI_CAPABILITY_LIST = 0x34
59 PCI_CB_BRIDGE_CONTROL = 0x3e
60 PCI_BRIDGE_CTL_BUS_RESET= 0x40
62 PCI_CAP_ID_EXP = 0x10
63 PCI_EXP_FLAGS = 0x2
64 PCI_EXP_FLAGS_TYPE = 0x00f0
65 PCI_EXP_TYPE_PCI_BRIDGE = 0x7
66 PCI_EXP_DEVCAP = 0x4
67 PCI_EXP_DEVCAP_FLR = (0x1 << 28)
68 PCI_EXP_DEVCTL = 0x8
69 PCI_EXP_DEVCTL_FLR = (0x1 << 15)
71 PCI_CAP_ID_PM = 0x01
72 PCI_PM_CTRL = 4
73 PCI_PM_CTRL_NO_SOFT_RESET = 0x0008
74 PCI_PM_CTRL_STATE_MASK = 0x0003
75 PCI_D3hot = 3
76 PCI_D0hot = 0
78 VENDOR_INTEL = 0x8086
79 PCI_CAP_ID_VENDOR_SPECIFIC_CAP = 0x09
80 PCI_CLASS_ID_USB = 0x0c03
81 PCI_USB_FLRCTRL = 0x4
83 PCI_CAP_ID_AF = 0x13
84 PCI_AF_CAPs = 0x3
85 PCI_AF_CAPs_TP_FLR = 0x3
86 PCI_AF_CTL = 0x4
87 PCI_AF_CTL_FLR = 0x1
89 PCI_BAR_0 = 0x10
90 PCI_BAR_5 = 0x24
91 PCI_BAR_SPACE = 0x01
92 PCI_BAR_IO = 0x01
93 PCI_BAR_IO_MASK = ~0x03
94 PCI_BAR_MEM = 0x00
95 PCI_BAR_MEM_MASK = ~0x0f
96 PCI_STATUS_CAP_MASK = 0x10
97 PCI_STATUS_OFFSET = 0x6
98 PCI_CAP_OFFSET = 0x34
99 MSIX_BIR_MASK = 0x7
100 MSIX_SIZE_MASK = 0x7ff
102 # Global variable to store information from lspci
103 lspci_info = None
104 lspci_info_lock = threading.RLock()
106 #Calculate PAGE_SHIFT: number of bits to shift an address to get the page number
107 PAGE_SIZE = resource.getpagesize()
108 PAGE_SHIFT = 0
109 t = PAGE_SIZE
110 while not (t&1):
111 t>>=1
112 PAGE_SHIFT+=1
114 PAGE_MASK=~(PAGE_SIZE - 1)
115 # Definitions from Linux: include/linux/pci.h
116 def PCI_DEVFN(slot, func):
117 return ((((slot) & 0x1f) << 3) | ((func) & 0x07))
119 def PCI_BDF(domain, bus, slot, func):
120 return (((domain & 0xffff) << 16) | ((bus & 0xff) << 8) |
121 PCI_DEVFN(slot, func))
123 def check_pci_opts(opts):
124 def f((k, v)):
125 if k not in ['msitranslate', 'power_mgmt'] or \
126 not v.lower() in ['0', '1', 'yes', 'no']:
127 raise PciDeviceParseError('Invalid pci option %s=%s: ' % (k, v))
129 map(f, opts)
131 def serialise_pci_opts(opts):
132 return ','.join(map(lambda x: '='.join(x), opts))
134 def split_pci_opts(opts):
135 return map(lambda x: x.split('='),
136 filter(lambda x: x != '', opts.split(',')))
138 def pci_opts_list_to_sxp(list):
139 return dev_dict_to_sxp({'opts': list})
141 def pci_opts_list_from_sxp(dev):
142 return map(lambda x: sxp.children(x)[0], sxp.children(dev, 'opts'))
144 def pci_convert_dict_to_sxp(dev, state, sub_state = None):
145 pci_sxp = ['pci', dev_dict_to_sxp(dev), ['state', state]]
146 if sub_state != None:
147 pci_sxp.append(['sub_state', sub_state])
148 return pci_sxp
150 def pci_convert_sxp_to_dict(dev_sxp):
151 """Convert pci device sxp to dict
152 @param dev_sxp: device configuration
153 @type dev_sxp: SXP object (parsed config)
154 @return: dev_config
155 @rtype: dictionary
156 """
157 # Parsing the device SXP's. In most cases, the SXP looks
158 # like this:
159 #
160 # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
161 #
162 # However, for PCI devices it looks like this:
163 #
164 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1], [func, 2]]]
165 #
166 # It seems the reasoning for this difference is because
167 # pciif.py needs all the PCI device configurations at
168 # the same time when creating the devices.
169 #
170 # To further complicate matters, Xen 2.0 configuration format
171 # uses the following for pci device configuration:
172 #
173 # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
175 # For PCI device hotplug support, the SXP of PCI devices is
176 # extendend like this:
177 #
178 # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1], [func, 2],
179 # [vslot, 0]],
180 # [state, 'Initialising']]]
181 #
182 # 'vslot' shows the virtual hotplug slot number which the PCI device
183 # is inserted in. This is only effective for HVM domains.
184 #
185 # state 'Initialising' indicates that the device is being attached,
186 # while state 'Closing' indicates that the device is being detached.
187 #
188 # The Dict looks like this:
189 #
190 # { devs: [{domain: 0, bus: 0, slot: 1, func: 2, vslot: 0}],
191 # states: ['Initialising'] }
193 dev_config = {}
195 pci_devs = []
196 for pci_dev in sxp.children(dev_sxp, 'dev'):
197 pci_dev_info = dict(pci_dev[1:])
198 if 'opts' in pci_dev_info:
199 pci_dev_info['opts'] = pci_opts_list_from_sxp(pci_dev)
200 # append uuid to each pci device that does't already have one.
201 if not pci_dev_info.has_key('uuid'):
202 dpci_uuid = pci_dev_info.get('uuid', uuid.createString())
203 pci_dev_info['uuid'] = dpci_uuid
204 pci_devs.append(pci_dev_info)
205 dev_config['devs'] = pci_devs
207 pci_states = []
208 for pci_state in sxp.children(dev_sxp, 'state'):
209 try:
210 pci_states.append(pci_state[1])
211 except IndexError:
212 raise XendError("Error reading state while parsing pci sxp")
213 dev_config['states'] = pci_states
215 return dev_config
217 def parse_hex(val):
218 try:
219 if isinstance(val, types.StringTypes):
220 return int(val, 16)
221 else:
222 return val
223 except ValueError:
224 return None
226 def parse_pci_name_extended(pci_dev_str):
227 pci_match = re.match(r"((?P<domain>[0-9a-fA-F]{1,4})[:,])?" +
228 r"(?P<bus>[0-9a-fA-F]{1,2})[:,]" +
229 r"(?P<slot>[0-9a-fA-F]{1,2})[.,]" +
230 r"(?P<func>(\*|[0-7]))" +
231 r"(@(?P<vslot>[01]?[0-9a-fA-F]))?" +
232 r"(,(?P<opts>.*))?$", pci_dev_str)
234 if pci_match == None:
235 raise PciDeviceParseError("Failed to parse pci device: %s" %
236 pci_dev_str)
238 out = {}
239 pci_dev_info = pci_match.groupdict('')
240 if pci_dev_info['domain'] == '':
241 domain = 0
242 else:
243 domain = int(pci_dev_info['domain'], 16)
244 out['domain'] = "0x%04x" % domain
245 out['bus'] = "0x%02x" % int(pci_dev_info['bus'], 16)
246 out['slot'] = "0x%02x" % int(pci_dev_info['slot'], 16)
247 out['func'] = "0x%x" % int(pci_dev_info['func'], 16)
248 if pci_dev_info['vslot'] == '':
249 vslot = AUTO_PHP_SLOT
250 else:
251 vslot = int(pci_dev_info['vslot'], 16)
252 out['vslot'] = "0x%02x" % vslot
253 if pci_dev_info['opts'] != '':
254 out['opts'] = split_pci_opts(pci_dev_info['opts'])
255 check_pci_opts(out['opts'])
257 return out
259 def parse_pci_name(pci_name_string):
260 pci = parse_pci_name_extended(pci_name_string)
262 if int(pci['vslot'], 16) != AUTO_PHP_SLOT:
263 raise PciDeviceParseError(("Failed to parse pci device: %s: " +
264 "vslot provided where prohibited: %s") %
265 (pci_name_string, pci['vslot']))
266 if 'opts' in pci:
267 raise PciDeviceParseError(("Failed to parse pci device: %s: " +
268 "options provided where prohibited: %s") %
269 (pci_name_string, pci['opts']))
271 return pci
273 def __pci_dict_to_fmt_str(fmt, dev):
274 return fmt % (int(dev['domain'], 16), int(dev['bus'], 16),
275 int(dev['slot'], 16), int(dev['func'], 16))
277 def pci_dict_to_bdf_str(dev):
278 return __pci_dict_to_fmt_str('%04x:%02x:%02x.%01x', dev)
280 def pci_dict_to_xc_str(dev):
281 return __pci_dict_to_fmt_str('0x%x, 0x%x, 0x%x, 0x%x', dev)
283 def pci_dict_cmp(a, b, keys=['domain', 'bus', 'slot', 'func']):
284 return reduce(lambda x, y: x and y,
285 map(lambda k: int(a[k], 16) == int(b[k], 16), keys))
287 def extract_the_exact_pci_names(pci_names):
288 result = []
290 if isinstance(pci_names, types.StringTypes):
291 pci_names = pci_names.split()
292 elif isinstance(pci_names, types.ListType):
293 pci_names = re.findall(PCI_DEV_REG_EXPRESS_STR, '%s' % pci_names)
294 else:
295 raise PciDeviceParseError('Invalid argument: %s' % pci_names)
297 for pci in pci_names:
298 # The length of DDDD:bb:dd.f is 12.
299 if len(pci) != 12:
300 continue
301 if re.match(PCI_DEV_REG_EXPRESS_STR, pci) is None:
302 continue
303 result = result + [pci]
304 return result
306 def find_sysfs_mnt():
307 try:
308 return utils.find_sysfs_mount()
309 except IOError, (errno, strerr):
310 raise PciDeviceParseError(('Failed to locate sysfs mount: %s: %s (%d)'%
311 (PROC_PCI_PATH, strerr, errno)))
312 return None
314 def get_all_pci_names():
315 sysfs_mnt = find_sysfs_mnt()
316 pci_names = os.popen('ls ' + sysfs_mnt + SYSFS_PCI_DEVS_PATH).read().split()
317 return pci_names
319 def get_all_pci_devices():
320 return map(PciDevice, map(parse_pci_name, get_all_pci_names()))
322 def _create_lspci_info():
323 """Execute 'lspci' command and parse the result.
324 If the command does not exist, lspci_info will be kept blank ({}).
326 Expects to be protected by lspci_info_lock.
327 """
328 global lspci_info
330 lspci_info = {}
332 for paragraph in os.popen(LSPCI_CMD + ' -vmm').read().split('\n\n'):
333 device_name = None
334 device_info = {}
335 # FIXME: workaround for pciutils without the -mm option.
336 # see: git://git.kernel.org/pub/scm/utils/pciutils/pciutils.git
337 # commit: 3fd6b4d2e2fda814047664ffc67448ac782a8089
338 first_device = True
339 for line in paragraph.split('\n'):
340 try:
341 (opt, value) = line.split(':\t')
342 if opt == 'Slot' or (opt == 'Device' and first_device):
343 device_name = pci_dict_to_bdf_str(parse_pci_name(value))
344 first_device = False
345 else:
346 device_info[opt] = value
347 except:
348 pass
349 if device_name is not None:
350 lspci_info[device_name] = device_info
352 def create_lspci_info():
353 global lspci_info_lock
354 lspci_info_lock.acquire()
355 try:
356 _create_lspci_info()
357 finally:
358 lspci_info_lock.release()
360 def save_pci_conf_space(devs_string):
361 pci_list = []
362 cfg_list = []
363 sysfs_mnt = find_sysfs_mnt()
364 for pci_str in devs_string:
365 pci_path = sysfs_mnt + SYSFS_PCI_DEVS_PATH + '/' + pci_str + \
366 SYSFS_PCI_DEV_CONFIG_PATH
367 fd = os.open(pci_path, os.O_RDONLY)
368 configs = []
369 for i in range(0, 256, 4):
370 configs = configs + [os.read(fd,4)]
371 os.close(fd)
372 pci_list = pci_list + [pci_path]
373 cfg_list = cfg_list + [configs]
374 return (pci_list, cfg_list)
376 def restore_pci_conf_space(pci_cfg_list):
377 pci_list = pci_cfg_list[0]
378 cfg_list = pci_cfg_list[1]
379 for i in range(0, len(pci_list)):
380 pci_path = pci_list[i]
381 configs = cfg_list[i]
382 fd = os.open(pci_path, os.O_WRONLY)
383 for dw in configs:
384 os.write(fd, dw)
385 os.close(fd)
387 def find_all_devices_owned_by_pciback():
388 sysfs_mnt = find_sysfs_mnt()
389 pciback_path = sysfs_mnt + SYSFS_PCIBACK_PATH
390 pci_names = os.popen('ls ' + pciback_path).read()
391 pci_list = extract_the_exact_pci_names(pci_names)
392 dev_list = []
393 for pci in pci_list:
394 dev = PciDevice(parse_pci_name(pci))
395 dev_list = dev_list + [dev]
396 return dev_list
398 def transform_list(target, src):
399 ''' src: its element is pci string (Format: xxxx:xx:xx.x).
400 target: its element is pci string, or a list of pci string.
402 If all the elements in src are in target, we remove them from target
403 and add src into target; otherwise, we remove from target all the
404 elements that also appear in src.
405 '''
406 result = []
407 target_contains_src = True
408 for e in src:
409 if not e in target:
410 target_contains_src = False
411 break
413 if target_contains_src:
414 result = result + [src]
415 for e in target:
416 if not e in src:
417 result = result + [e]
418 return result
420 def check_FLR_capability(dev_list):
421 if len(dev_list) == 0:
422 return []
424 pci_list = []
425 pci_dev_dict = {}
426 for dev in dev_list:
427 pci_list = pci_list + [dev.name]
428 pci_dev_dict[dev.name] = dev
430 while True:
431 need_transform = False
432 for pci in pci_list:
433 if isinstance(pci, types.StringTypes):
434 dev = pci_dev_dict[pci]
435 if dev.bus == 0:
436 continue
437 if dev.dev_type == DEV_TYPE_PCIe_ENDPOINT and not dev.pcie_flr:
438 coassigned_pci_list = dev.find_all_the_multi_functions()
439 need_transform = True
440 elif dev.dev_type == DEV_TYPE_PCI and not dev.pci_af_flr:
441 coassigned_pci_list = dev.find_coassigned_pci_devices(True)
442 del coassigned_pci_list[0]
443 need_transform = True
445 if need_transform:
446 pci_list = transform_list(pci_list, coassigned_pci_list)
447 if not need_transform:
448 break
450 if len(pci_list) == 0:
451 return []
453 for i in range(0, len(pci_list)):
454 if isinstance(pci_list[i], types.StringTypes):
455 pci_list[i] = [pci_list[i]]
457 # Now every element in pci_list is a list of pci string.
459 result = []
460 for pci_names in pci_list:
461 devs = []
462 for pci in pci_names:
463 devs = devs + [pci_dev_dict[pci]]
464 result = result + [devs]
465 return result
467 def check_mmio_bar(devs_list):
468 result = []
470 for dev_list in devs_list:
471 non_aligned_bar_found = False
472 for dev in dev_list:
473 if dev.has_non_page_aligned_bar:
474 non_aligned_bar_found = True
475 break
476 if not non_aligned_bar_found:
477 result = result + [dev_list]
479 return result
481 class PciDeviceParseError(Exception):
482 def __init__(self,msg):
483 self.message = msg
484 def __str__(self):
485 return 'Error Parsing PCI Device Info: '+self.message
487 class PciDeviceAssignmentError(Exception):
488 def __init__(self,msg):
489 self.message = msg
490 def __str__(self):
491 return 'pci: improper device assignment specified: ' + \
492 self.message
494 class PciDeviceVslotMissing(Exception):
495 def __init__(self,msg):
496 self.message = msg
497 def __str__(self):
498 return 'pci: no vslot: ' + self.message
500 class PciDevice:
501 def __init__(self, dev):
502 self.domain = int(dev['domain'], 16)
503 self.bus = int(dev['bus'], 16)
504 self.slot = int(dev['slot'], 16)
505 self.func = int(dev['func'], 16)
506 self.name = pci_dict_to_bdf_str(dev)
507 self.cfg_space_path = find_sysfs_mnt()+SYSFS_PCI_DEVS_PATH+'/'+ \
508 self.name + SYSFS_PCI_DEV_CONFIG_PATH
509 self.irq = 0
510 self.iomem = []
511 self.ioports = []
512 self.driver = None
513 self.vendor = None
514 self.device = None
515 self.subvendor = None
516 self.subdevice = None
517 self.msix = 0
518 self.msix_iomem = []
519 self.revision = 0
520 self.classcode = None
521 self.vendorname = ""
522 self.devicename = ""
523 self.classname = ""
524 self.subvendorname = ""
525 self.subdevicename = ""
526 self.dev_type = None
527 self.has_non_page_aligned_bar = False
528 self.pcie_flr = False
529 self.pci_af_flr = False
530 self.detect_dev_info()
531 self.get_info_from_sysfs()
532 self.get_info_from_lspci()
534 def find_parent(self):
535 # i.e., /sys/bus/pci/devices/0000:00:19.0 or
536 # /sys/bus/pci/devices/0000:03:04.0
537 path = find_sysfs_mnt()+SYSFS_PCI_DEVS_PATH+'/'+ self.name
538 # i.e., ../../../devices/pci0000:00/0000:00:19.0
539 # ../../../devices/pci0000:00/0000:00:02.0/0000:01:00.2/0000:03:04.0
540 try:
541 target = os.readlink(path)
542 lst = target.split('/')
543 parent = lst[len(lst)-2]
544 if parent[0:3] == 'pci':
545 # We have reached the upmost one.
546 return None
547 else:
548 dev = {}
549 lst = parent.split(':')
550 dev['domain'] = '%04x' % int(lst[0], 16)
551 dev['bus'] = '%02x' % int(lst[1], 16)
552 lst = lst[2]
553 lst = lst.split('.')
554 dev['slot'] = '%02x' % int(lst[0], 16)
555 dev['func'] = '%x' % int(lst[1], 16)
556 return dev
557 except OSError, (errno, strerr):
558 raise PciDeviceParseError('Can not locate the parent of %s',
559 self.name)
561 def find_the_uppermost_pci_bridge(self):
562 # Find the uppermost PCI/PCI-X bridge
563 dev = self.find_parent()
564 if dev is None:
565 return None
566 dev = dev_parent = PciDevice(dev)
567 while dev_parent.dev_type != DEV_TYPE_PCIe_BRIDGE:
568 parent = dev_parent.find_parent()
569 if parent is None:
570 break
571 dev = dev_parent
572 dev_parent = PciDevice(parent)
573 return dev
575 def find_all_devices_behind_the_bridge(self, ignore_bridge):
576 sysfs_mnt = find_sysfs_mnt()
577 self_path = sysfs_mnt + SYSFS_PCI_DEVS_PATH + '/' + self.name
578 pci_names = os.popen('ls ' + self_path).read()
579 dev_list = extract_the_exact_pci_names(pci_names)
581 list = [self.name]
582 for pci_str in dev_list:
583 dev = PciDevice(parse_pci_name(pci_str))
584 if dev.dev_type == DEV_TYPE_PCI_BRIDGE or \
585 dev.dev_type == DEV_TYPE_PCIe_BRIDGE:
586 sub_list_including_self = \
587 dev.find_all_devices_behind_the_bridge(ignore_bridge)
588 if ignore_bridge:
589 del sub_list_including_self[0]
590 list = list + [sub_list_including_self]
591 else:
592 list = list + [dev.name]
593 return list
595 def find_coassigned_pci_devices(self, ignore_bridge = True):
596 ''' Here'self' is a PCI device, we need find the uppermost PCI/PCI-X
597 bridge, and all devices behind it must be co-assigned to the same
598 guest.
600 Parameter:
601 [ignore_bridge]: if set, the returned result doesn't include
602 any bridge behind the uppermost PCI/PCI-X bridge.
604 Note: The first element of the return value is the uppermost
605 PCI/PCI-X bridge. If the caller doesn't need the first
606 element, the caller itself can remove it explicitly.
607 '''
608 dev = self.find_the_uppermost_pci_bridge()
610 # The 'self' device is on bus0.
611 if dev is None:
612 return [self.name]
614 dev_list = dev.find_all_devices_behind_the_bridge(ignore_bridge)
615 dev_list = extract_the_exact_pci_names(dev_list)
616 return dev_list
618 def do_secondary_bus_reset(self, target_bus, devs):
619 # Save the config spaces of all the devices behind the bus.
620 (pci_list, cfg_list) = save_pci_conf_space(devs)
622 #Do the Secondary Bus Reset
623 sysfs_mnt = find_sysfs_mnt()
624 parent_path = sysfs_mnt + SYSFS_PCI_DEVS_PATH + '/' + \
625 target_bus + SYSFS_PCI_DEV_CONFIG_PATH
626 fd = os.open(parent_path, os.O_RDWR)
627 os.lseek(fd, PCI_CB_BRIDGE_CONTROL, 0)
628 br_cntl = (struct.unpack('H', os.read(fd, 2)))[0]
629 # Assert Secondary Bus Reset
630 os.lseek(fd, PCI_CB_BRIDGE_CONTROL, 0)
631 br_cntl |= PCI_BRIDGE_CTL_BUS_RESET
632 os.write(fd, struct.pack('H', br_cntl))
633 time.sleep(0.100)
634 # De-assert Secondary Bus Reset
635 os.lseek(fd, PCI_CB_BRIDGE_CONTROL, 0)
636 br_cntl &= ~PCI_BRIDGE_CTL_BUS_RESET
637 os.write(fd, struct.pack('H', br_cntl))
638 time.sleep(0.100)
639 os.close(fd)
641 # Restore the config spaces
642 restore_pci_conf_space((pci_list, cfg_list))
644 def do_Dstate_transition(self):
645 pos = self.find_cap_offset(PCI_CAP_ID_PM)
646 if pos == 0:
647 return False
649 # No_Soft_Reset - When set 1, this bit indicates that
650 # devices transitioning from D3hot to D0 because of
651 # PowerState commands do not perform an internal reset.
652 pm_ctl = self.pci_conf_read32(pos + PCI_PM_CTRL)
653 if (pm_ctl & PCI_PM_CTRL_NO_SOFT_RESET) == 1:
654 return False
656 (pci_list, cfg_list) = save_pci_conf_space([self.name])
658 # Enter D3hot
659 pm_ctl &= ~PCI_PM_CTRL_STATE_MASK
660 pm_ctl |= PCI_D3hot
661 self.pci_conf_write32(pos + PCI_PM_CTRL, pm_ctl)
662 time.sleep(0.010)
664 # From D3hot to D0
665 pm_ctl &= ~PCI_PM_CTRL_STATE_MASK
666 pm_ctl |= PCI_D0hot
667 self.pci_conf_write32(pos + PCI_PM_CTRL, pm_ctl)
668 time.sleep(0.010)
670 restore_pci_conf_space((pci_list, cfg_list))
671 return True
673 def do_vendor_specific_FLR_method(self):
674 pos = self.find_cap_offset(PCI_CAP_ID_VENDOR_SPECIFIC_CAP)
675 if pos == 0:
676 return
678 vendor_id = self.pci_conf_read16(PCI_VENDOR_ID)
679 if vendor_id != VENDOR_INTEL:
680 return
682 class_id = self.pci_conf_read16(PCI_CLASS_DEVICE)
683 if class_id != PCI_CLASS_ID_USB:
684 return
686 (pci_list, cfg_list) = save_pci_conf_space([self.name])
688 self.pci_conf_write8(pos + PCI_USB_FLRCTRL, 1)
689 time.sleep(0.100)
691 restore_pci_conf_space((pci_list, cfg_list))
693 def do_FLR_for_integrated_device(self):
694 if not self.do_Dstate_transition():
695 self.do_vendor_specific_FLR_method()
697 def find_all_the_multi_functions(self):
698 sysfs_mnt = find_sysfs_mnt()
699 parent = pci_dict_to_bdf_str(self.find_parent())
700 pci_names = os.popen('ls ' + sysfs_mnt + SYSFS_PCI_DEVS_PATH + '/' + \
701 parent + '/').read()
702 funcs = extract_the_exact_pci_names(pci_names)
703 return funcs
705 def find_coassigned_devices(self):
706 if self.dev_type == DEV_TYPE_PCIe_ENDPOINT and not self.pcie_flr:
707 return self.find_all_the_multi_functions()
708 elif self.dev_type == DEV_TYPE_PCI and not self.pci_af_flr:
709 coassigned_pci_list = self.find_coassigned_pci_devices(True)
710 if len(coassigned_pci_list) > 1:
711 del coassigned_pci_list[0]
712 return coassigned_pci_list
713 else:
714 return [self.name]
716 def find_cap_offset(self, cap):
717 path = find_sysfs_mnt()+SYSFS_PCI_DEVS_PATH+'/'+ \
718 self.name+SYSFS_PCI_DEV_CONFIG_PATH
720 pos = PCI_CAPABILITY_LIST
722 try:
723 fd = os.open(path, os.O_RDONLY)
724 os.lseek(fd, PCI_STATUS, 0)
725 status = struct.unpack('H', os.read(fd, 2))[0]
726 if (status & 0x10) == 0:
727 # The device doesn't support PCI_STATUS_CAP_LIST
728 return 0
730 max_cap = 48
731 while max_cap > 0:
732 os.lseek(fd, pos, 0)
733 pos = ord(os.read(fd, 1))
734 if pos < 0x40:
735 pos = 0
736 break;
737 os.lseek(fd, pos + 0, 0)
738 id = ord(os.read(fd, 1))
739 if id == 0xff:
740 pos = 0
741 break;
743 # Found the capability
744 if id == cap:
745 break;
747 # Test the next one
748 pos = pos + 1
749 max_cap = max_cap - 1;
751 os.close(fd)
752 except OSError, (errno, strerr):
753 raise PciDeviceParseError(('Error when accessing sysfs: %s (%d)' %
754 (strerr, errno)))
755 return pos
757 def pci_conf_read8(self, pos):
758 fd = os.open(self.cfg_space_path, os.O_RDONLY)
759 os.lseek(fd, pos, 0)
760 str = os.read(fd, 1)
761 os.close(fd)
762 val = struct.unpack('B', str)[0]
763 return val
765 def pci_conf_read16(self, pos):
766 fd = os.open(self.cfg_space_path, os.O_RDONLY)
767 os.lseek(fd, pos, 0)
768 str = os.read(fd, 2)
769 os.close(fd)
770 val = struct.unpack('H', str)[0]
771 return val
773 def pci_conf_read32(self, pos):
774 fd = os.open(self.cfg_space_path, os.O_RDONLY)
775 os.lseek(fd, pos, 0)
776 str = os.read(fd, 4)
777 os.close(fd)
778 val = struct.unpack('I', str)[0]
779 return val
781 def pci_conf_write8(self, pos, val):
782 str = struct.pack('B', val)
783 fd = os.open(self.cfg_space_path, os.O_WRONLY)
784 os.lseek(fd, pos, 0)
785 os.write(fd, str)
786 os.close(fd)
788 def pci_conf_write16(self, pos, val):
789 str = struct.pack('H', val)
790 fd = os.open(self.cfg_space_path, os.O_WRONLY)
791 os.lseek(fd, pos, 0)
792 os.write(fd, str)
793 os.close(fd)
795 def pci_conf_write32(self, pos, val):
796 str = struct.pack('I', val)
797 fd = os.open(self.cfg_space_path, os.O_WRONLY)
798 os.lseek(fd, pos, 0)
799 os.write(fd, str)
800 os.close(fd)
802 def detect_dev_info(self):
803 class_dev = self.pci_conf_read16(PCI_CLASS_DEVICE)
804 pos = self.find_cap_offset(PCI_CAP_ID_EXP)
805 if class_dev == PCI_CLASS_BRIDGE_PCI:
806 if pos == 0:
807 self.dev_type = DEV_TYPE_PCI_BRIDGE
808 else:
809 creg = self.pci_conf_read16(pos + PCI_EXP_FLAGS)
810 if ((creg & PCI_EXP_FLAGS_TYPE) >> 4) == \
811 PCI_EXP_TYPE_PCI_BRIDGE:
812 self.dev_type = DEV_TYPE_PCI_BRIDGE
813 else:
814 self.dev_type = DEV_TYPE_PCIe_BRIDGE
815 else:
816 if pos != 0:
817 self.dev_type = DEV_TYPE_PCIe_ENDPOINT
818 else:
819 self.dev_type = DEV_TYPE_PCI
821 # Force 0000:00:00.0 to be DEV_TYPE_PCIe_BRIDGE
822 if self.name == '0000:00:00.0':
823 self.dev_type = DEV_TYPE_PCIe_BRIDGE
825 if (self.dev_type == DEV_TYPE_PCI_BRIDGE) or \
826 (self.dev_type == DEV_TYPE_PCIe_BRIDGE):
827 return
829 # Try to findthe PCIe FLR capability
830 if self.dev_type == DEV_TYPE_PCIe_ENDPOINT:
831 dev_cap = self.pci_conf_read32(pos + PCI_EXP_DEVCAP)
832 if dev_cap & PCI_EXP_DEVCAP_FLR:
833 self.pcie_flr = True
834 elif self.dev_type == DEV_TYPE_PCI:
835 # Try to find the "PCI Advanced Capabilities"
836 pos = self.find_cap_offset(PCI_CAP_ID_AF)
837 if pos != 0:
838 af_cap = self.pci_conf_read8(pos + PCI_AF_CAPs)
839 if (af_cap & PCI_AF_CAPs_TP_FLR) == PCI_AF_CAPs_TP_FLR:
840 self.pci_af_flr = True
842 bar_addr = PCI_BAR_0
843 while bar_addr <= PCI_BAR_5:
844 bar = self.pci_conf_read32(bar_addr)
845 if (bar & PCI_BAR_SPACE) == PCI_BAR_MEM:
846 bar = bar & PCI_BAR_MEM_MASK
847 bar = bar & ~PAGE_MASK
848 if bar != 0:
849 self.has_non_page_aligned_bar = True
850 break
851 bar_addr = bar_addr + 4
853 def devs_check_driver(self, devs):
854 if len(devs) == 0:
855 return
856 for pci_dev in devs:
857 dev = PciDevice(parse_pci_name(pci_dev))
858 if dev.driver == 'pciback':
859 continue
860 err_msg = 'pci: %s must be co-assigned to the same guest with %s' + \
861 ', but it is not owned by pciback.'
862 raise PciDeviceAssignmentError(err_msg % (pci_dev, self.name))
864 def do_FLR(self):
865 """ Perform FLR (Functional Level Reset) for the device.
866 """
867 if self.dev_type == DEV_TYPE_PCIe_ENDPOINT:
868 # If PCIe device supports FLR, we use it.
869 if self.pcie_flr:
870 (pci_list, cfg_list) = save_pci_conf_space([self.name])
871 pos = self.find_cap_offset(PCI_CAP_ID_EXP)
872 self.pci_conf_write32(pos + PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_FLR)
873 # We must sleep at least 100ms for the completion of FLR
874 time.sleep(0.100)
875 restore_pci_conf_space((pci_list, cfg_list))
876 else:
877 if self.bus == 0:
878 self.do_FLR_for_integrated_device()
879 else:
880 funcs = self.find_all_the_multi_functions()
881 self.devs_check_driver(funcs)
883 parent = pci_dict_to_bdf_str(self.find_parent())
885 # Do Secondary Bus Reset.
886 self.do_secondary_bus_reset(parent, funcs)
887 # PCI devices
888 else:
889 # For PCI device on host bus, we test "PCI Advanced Capabilities".
890 if self.bus == 0 and self.pci_af_flr:
891 (pci_list, cfg_list) = save_pci_conf_space([self.name])
892 # We use Advanced Capability to do FLR.
893 pos = self.find_cap_offset(PCI_CAP_ID_AF)
894 self.pci_conf_write8(pos + PCI_AF_CTL, PCI_AF_CTL_FLR)
895 time.sleep(0.100)
896 restore_pci_conf_space((pci_list, cfg_list))
897 else:
898 if self.bus == 0:
899 self.do_FLR_for_integrated_device()
900 else:
901 devs = self.find_coassigned_pci_devices(False)
902 # Remove the element 0 which is a bridge
903 target_bus = devs[0]
904 del devs[0]
905 self.devs_check_driver(devs)
907 # Do Secondary Bus Reset.
908 self.do_secondary_bus_reset(target_bus, devs)
910 def find_capability(self, type):
911 sysfs_mnt = find_sysfs_mnt()
912 if sysfs_mnt == None:
913 return False
914 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
915 self.name+SYSFS_PCI_DEV_CONFIG_PATH
916 try:
917 conf_file = open(path, 'rb')
918 conf_file.seek(PCI_HEADER_TYPE)
919 header_type = ord(conf_file.read(1)) & PCI_HEADER_TYPE_MASK
920 if header_type == PCI_HEADER_TYPE_CARDBUS:
921 return
922 conf_file.seek(PCI_STATUS_OFFSET)
923 status = ord(conf_file.read(1))
924 if status&PCI_STATUS_CAP_MASK:
925 conf_file.seek(PCI_CAP_OFFSET)
926 capa_pointer = ord(conf_file.read(1))
927 capa_count = 0
928 while capa_pointer:
929 if capa_pointer < 0x40:
930 raise PciDeviceParseError(
931 ('Broken capability chain: %s' % self.name))
932 capa_count += 1
933 if capa_count > 96:
934 raise PciDeviceParseError(
935 ('Looped capability chain: %s' % self.name))
936 conf_file.seek(capa_pointer)
937 capa_id = ord(conf_file.read(1))
938 capa_pointer = ord(conf_file.read(1))
939 if capa_id == type:
940 # get the type
941 message_cont_lo = ord(conf_file.read(1))
942 message_cont_hi = ord(conf_file.read(1))
943 self.msix=1
944 self.msix_entries = (message_cont_lo + \
945 (message_cont_hi << 8)) \
946 & MSIX_SIZE_MASK
947 t_off=conf_file.read(4)
948 p_off=conf_file.read(4)
949 self.table_offset=ord(t_off[0]) | (ord(t_off[1])<<8) | \
950 (ord(t_off[2])<<16)| \
951 (ord(t_off[3])<<24)
952 self.pba_offset=ord(p_off[0]) | (ord(p_off[1]) << 8)| \
953 (ord(p_off[2])<<16) | \
954 (ord(p_off[3])<<24)
955 self.table_index = self.table_offset & MSIX_BIR_MASK
956 self.table_offset = self.table_offset & ~MSIX_BIR_MASK
957 self.pba_index = self.pba_offset & MSIX_BIR_MASK
958 self.pba_offset = self.pba_offset & ~MSIX_BIR_MASK
959 break
960 except IOError, (errno, strerr):
961 raise PciDeviceParseError(('Failed to locate sysfs mount: %s: %s (%d)' %
962 (PROC_PCI_PATH, strerr, errno)))
964 def remove_msix_iomem(self, index, start, size):
965 if (index == self.table_index):
966 table_start = start+self.table_offset
967 table_end = table_start + self.msix_entries * 16
968 table_start = table_start & PAGE_MASK
969 table_end = (table_end + PAGE_SIZE) & PAGE_MASK
970 self.msix_iomem.append((table_start, table_end-table_start))
971 if (index==self.pba_index):
972 pba_start = start + self.pba_offset
973 pba_end = pba_start + self.msix_entries/8
974 pba_start = pba_start & PAGE_MASK
975 pba_end = (pba_end + PAGE_SIZE) & PAGE_MASK
976 self.msix_iomem.append((pba_start, pba_end-pba_start))
978 def get_info_from_sysfs(self):
979 self.find_capability(0x11)
980 sysfs_mnt = find_sysfs_mnt()
981 if sysfs_mnt == None:
982 return False
984 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
985 self.name+SYSFS_PCI_DEV_RESOURCE_PATH
986 try:
987 resource_file = open(path,'r')
989 for i in range(PROC_PCI_NUM_RESOURCES):
990 line = resource_file.readline()
991 sline = line.split()
992 if len(sline)<3:
993 continue
995 start = int(sline[0],16)
996 end = int(sline[1],16)
997 flags = int(sline[2],16)
998 size = end-start+1
1000 if start!=0:
1001 if flags&PCI_BAR_IO:
1002 self.ioports.append( (start,size) )
1003 else:
1004 self.iomem.append( (start,size) )
1005 if (self.msix):
1006 self.remove_msix_iomem(i, start, size)
1010 except IOError, (errno, strerr):
1011 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
1012 (path, strerr, errno)))
1014 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
1015 self.name+SYSFS_PCI_DEV_IRQ_PATH
1016 try:
1017 self.irq = int(open(path,'r').readline())
1018 except IOError, (errno, strerr):
1019 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
1020 (path, strerr, errno)))
1022 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
1023 self.name+SYSFS_PCI_DEV_DRIVER_DIR_PATH
1024 try:
1025 self.driver = os.path.basename(os.readlink(path))
1026 except OSError, (errno, strerr):
1027 self.driver = ""
1029 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
1030 self.name+SYSFS_PCI_DEV_VENDOR_PATH
1031 try:
1032 self.vendor = int(open(path,'r').readline(), 16)
1033 except IOError, (errno, strerr):
1034 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
1035 (path, strerr, errno)))
1037 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
1038 self.name+SYSFS_PCI_DEV_DEVICE_PATH
1039 try:
1040 self.device = int(open(path,'r').readline(), 16)
1041 except IOError, (errno, strerr):
1042 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
1043 (path, strerr, errno)))
1045 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
1046 self.name+SYSFS_PCI_DEV_SUBVENDOR_PATH
1047 try:
1048 self.subvendor = int(open(path,'r').readline(), 16)
1049 except IOError, (errno, strerr):
1050 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
1051 (path, strerr, errno)))
1053 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
1054 self.name+SYSFS_PCI_DEV_SUBDEVICE_PATH
1055 try:
1056 self.subdevice = int(open(path,'r').readline(), 16)
1057 except IOError, (errno, strerr):
1058 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
1059 (path, strerr, errno)))
1061 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
1062 self.name+SYSFS_PCI_DEV_CLASS_PATH
1063 try:
1064 self.classcode = int(open(path,'r').readline(), 16)
1065 except IOError, (errno, strerr):
1066 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
1067 (path, strerr, errno)))
1069 return True
1071 def get_info_from_lspci(self):
1072 """ Get information such as vendor name, device name, class name, etc.
1073 Since we cannot obtain these data from sysfs, use 'lspci' command.
1074 """
1075 global lspci_info
1076 global lspci_info_lock
1078 lspci_info_lock.acquire()
1079 try:
1080 if lspci_info is None:
1081 _create_lspci_info()
1083 device_info = lspci_info.get(self.name)
1084 if device_info:
1085 try:
1086 self.revision = int(device_info.get('Rev', '0'), 16)
1087 except ValueError:
1088 pass
1089 self.vendorname = device_info.get('Vendor', '')
1090 self.devicename = device_info.get('Device', '')
1091 self.classname = device_info.get('Class', '')
1092 self.subvendorname = device_info.get('SVendor', '')
1093 self.subdevicename = device_info.get('SDevice', '')
1094 return True
1095 finally:
1096 lspci_info_lock.release()
1098 def __str__(self):
1099 str = "PCI Device %s\n" % (self.name)
1100 for (start,size) in self.ioports:
1101 str = str + "IO Port 0x%02x [size=%d]\n"%(start,size)
1102 for (start,size) in self.iomem:
1103 str = str + "IO Mem 0x%02x [size=%d]\n"%(start,size)
1104 str = str + "IRQ %d\n"%(self.irq)
1105 str = str + "Vendor ID 0x%04x\n"%(self.vendor)
1106 str = str + "Device ID 0x%04x\n"%(self.device)
1107 str = str + "Sybsystem Vendor ID 0x%04x\n"%(self.subvendor)
1108 str = str + "Subsystem Device ID 0x%04x"%(self.subdevice)
1109 return str
1111 def main():
1112 if len(sys.argv)<5:
1113 print "Usage: %s <domain> <bus> <slot> <func>\n" % sys.argv[0]
1114 sys.exit(2)
1116 dev = PciDevice(int(sys.argv[1],16), int(sys.argv[2],16),
1117 int(sys.argv[3],16), int(sys.argv[4],16))
1118 print str(dev)
1120 if __name__=='__main__':
1121 main()