ia64/xen-unstable

view tools/python/xen/util/pci.py @ 19759:11c3f4e786b3

xend: pass-through: Remove PciDeviceNotFoundError, it is never used

Signed-off-by: Simon Horman <horms@verge.net.au>
author Keir Fraser <keir.fraser@citrix.com>
date Tue Jun 16 11:38:33 2009 +0100 (2009-06-16)
parents 0573bbe19499
children 45ca3f3c3f98
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
19 PROC_PCI_PATH = '/proc/bus/pci/devices'
20 PROC_PCI_NUM_RESOURCES = 7
22 SYSFS_PCI_DEVS_PATH = '/bus/pci/devices'
23 SYSFS_PCI_DEV_RESOURCE_PATH = '/resource'
24 SYSFS_PCI_DEV_CONFIG_PATH = '/config'
25 SYSFS_PCI_DEV_IRQ_PATH = '/irq'
26 SYSFS_PCI_DEV_DRIVER_DIR_PATH = '/driver'
27 SYSFS_PCI_DEV_VENDOR_PATH = '/vendor'
28 SYSFS_PCI_DEV_DEVICE_PATH = '/device'
29 SYSFS_PCI_DEV_SUBVENDOR_PATH = '/subsystem_vendor'
30 SYSFS_PCI_DEV_SUBDEVICE_PATH = '/subsystem_device'
31 SYSFS_PCI_DEV_CLASS_PATH = '/class'
32 SYSFS_PCIBACK_PATH = '/bus/pci/drivers/pciback/'
34 LSPCI_CMD = 'lspci'
36 PCI_DEV_REG_EXPRESS_STR = r"[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}."+ \
37 r"[0-9a-fA-F]{1}"
38 PCI_DEV_FORMAT_STR = '%04x:%02x:%02x.%01x'
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(pci_name_string):
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])$", pci_name_string)
156 if pci_match is None:
157 raise PciDeviceParseError(('Failed to parse pci device name: %s' %
158 pci_name_string))
159 pci_dev_info = pci_match.groupdict('0')
161 domain = parse_hex(pci_dev_info['domain'])
162 bus = parse_hex(pci_dev_info['bus'])
163 slot = parse_hex(pci_dev_info['slot'])
164 func = parse_hex(pci_dev_info['func'])
166 return (domain, bus, slot, func)
168 def extract_the_exact_pci_names(pci_names):
169 result = []
171 if isinstance(pci_names, types.StringTypes):
172 pci_names = pci_names.split()
173 elif isinstance(pci_names, types.ListType):
174 pci_names = re.findall(PCI_DEV_REG_EXPRESS_STR, '%s' % pci_names)
175 else:
176 raise PciDeviceParseError('Invalid argument: %s' % pci_names)
178 for pci in pci_names:
179 # The length of DDDD:bb:dd.f is 12.
180 if len(pci) != 12:
181 continue
182 if re.match(PCI_DEV_REG_EXPRESS_STR, pci) is None:
183 continue
184 result = result + [pci]
185 return result
187 def find_sysfs_mnt():
188 try:
189 return utils.find_sysfs_mount()
190 except IOError, (errno, strerr):
191 raise PciDeviceParseError(('Failed to locate sysfs mount: %s: %s (%d)'%
192 (PROC_PCI_PATH, strerr, errno)))
193 return None
195 def get_all_pci_names():
196 sysfs_mnt = find_sysfs_mnt()
197 pci_names = os.popen('ls ' + sysfs_mnt + SYSFS_PCI_DEVS_PATH).read().split()
198 return pci_names
200 def get_all_pci_devices():
201 pci_devs = []
202 for pci_name in get_all_pci_names():
203 pci_match = re.match(r"((?P<domain>[0-9a-fA-F]{1,4})[:,])?" + \
204 r"(?P<bus>[0-9a-fA-F]{1,2})[:,]" + \
205 r"(?P<slot>[0-9a-fA-F]{1,2})[.,]" + \
206 r"(?P<func>[0-7])$", pci_name)
207 if pci_match is None:
208 raise PciDeviceParseError(('Failed to parse pci device name: %s' %
209 pci_name))
210 pci_dev_info = pci_match.groupdict('0')
211 domain = parse_hex(pci_dev_info['domain'])
212 bus = parse_hex(pci_dev_info['bus'])
213 slot = parse_hex(pci_dev_info['slot'])
214 func = parse_hex(pci_dev_info['func'])
215 try:
216 pci_dev = PciDevice(domain, bus, slot, func)
217 except:
218 continue
219 pci_devs.append(pci_dev)
221 return pci_devs
223 def _create_lspci_info():
224 """Execute 'lspci' command and parse the result.
225 If the command does not exist, lspci_info will be kept blank ({}).
227 Expects to be protected by lspci_info_lock.
228 """
229 global lspci_info
231 lspci_info = {}
233 for paragraph in os.popen(LSPCI_CMD + ' -vmm').read().split('\n\n'):
234 device_name = None
235 device_info = {}
236 for line in paragraph.split('\n'):
237 try:
238 (opt, value) = line.split(':\t')
239 if opt == 'Slot':
240 device_name = PCI_DEV_FORMAT_STR % parse_pci_name(value)
241 else:
242 device_info[opt] = value
243 except:
244 pass
245 if device_name is not None:
246 lspci_info[device_name] = device_info
248 def create_lspci_info():
249 global lspci_info_lock
250 lspci_info_lock.acquire()
251 try:
252 _create_lspci_info()
253 finally:
254 lspci_info_lock.release()
256 def save_pci_conf_space(devs_string):
257 pci_list = []
258 cfg_list = []
259 sysfs_mnt = find_sysfs_mnt()
260 for pci_str in devs_string:
261 pci_path = sysfs_mnt + SYSFS_PCI_DEVS_PATH + '/' + pci_str + \
262 SYSFS_PCI_DEV_CONFIG_PATH
263 fd = os.open(pci_path, os.O_RDONLY)
264 configs = []
265 for i in range(0, 256, 4):
266 configs = configs + [os.read(fd,4)]
267 os.close(fd)
268 pci_list = pci_list + [pci_path]
269 cfg_list = cfg_list + [configs]
270 return (pci_list, cfg_list)
272 def restore_pci_conf_space(pci_cfg_list):
273 pci_list = pci_cfg_list[0]
274 cfg_list = pci_cfg_list[1]
275 for i in range(0, len(pci_list)):
276 pci_path = pci_list[i]
277 configs = cfg_list[i]
278 fd = os.open(pci_path, os.O_WRONLY)
279 for dw in configs:
280 os.write(fd, dw)
281 os.close(fd)
283 def find_all_devices_owned_by_pciback():
284 sysfs_mnt = find_sysfs_mnt()
285 pciback_path = sysfs_mnt + SYSFS_PCIBACK_PATH
286 pci_names = os.popen('ls ' + pciback_path).read()
287 pci_list = extract_the_exact_pci_names(pci_names)
288 dev_list = []
289 for pci in pci_list:
290 (dom, b, d, f) = parse_pci_name(pci)
291 dev = PciDevice(dom, b, d, f)
292 dev_list = dev_list + [dev]
293 return dev_list
295 def transform_list(target, src):
296 ''' src: its element is pci string (Format: xxxx:xx:xx.x).
297 target: its element is pci string, or a list of pci string.
299 If all the elements in src are in target, we remove them from target
300 and add src into target; otherwise, we remove from target all the
301 elements that also appear in src.
302 '''
303 result = []
304 target_contains_src = True
305 for e in src:
306 if not e in target:
307 target_contains_src = False
308 break
310 if target_contains_src:
311 result = result + [src]
312 for e in target:
313 if not e in src:
314 result = result + [e]
315 return result
317 def check_FLR_capability(dev_list):
318 if len(dev_list) == 0:
319 return []
321 pci_list = []
322 pci_dev_dict = {}
323 for dev in dev_list:
324 pci_list = pci_list + [dev.name]
325 pci_dev_dict[dev.name] = dev
327 while True:
328 need_transform = False
329 for pci in pci_list:
330 if isinstance(pci, types.StringTypes):
331 dev = pci_dev_dict[pci]
332 if dev.bus == 0:
333 continue
334 if dev.dev_type == DEV_TYPE_PCIe_ENDPOINT and not dev.pcie_flr:
335 coassigned_pci_list = dev.find_all_the_multi_functions()
336 need_transform = True
337 elif dev.dev_type == DEV_TYPE_PCI and not dev.pci_af_flr:
338 coassigned_pci_list = dev.find_coassigned_pci_devices(True)
339 del coassigned_pci_list[0]
340 need_transform = True
342 if need_transform:
343 pci_list = transform_list(pci_list, coassigned_pci_list)
344 if not need_transform:
345 break
347 if len(pci_list) == 0:
348 return []
350 for i in range(0, len(pci_list)):
351 if isinstance(pci_list[i], types.StringTypes):
352 pci_list[i] = [pci_list[i]]
354 # Now every element in pci_list is a list of pci string.
356 result = []
357 for pci_names in pci_list:
358 devs = []
359 for pci in pci_names:
360 devs = devs + [pci_dev_dict[pci]]
361 result = result + [devs]
362 return result
364 def check_mmio_bar(devs_list):
365 result = []
367 for dev_list in devs_list:
368 non_aligned_bar_found = False
369 for dev in dev_list:
370 if dev.has_non_page_aligned_bar:
371 non_aligned_bar_found = True
372 break
373 if not non_aligned_bar_found:
374 result = result + [dev_list]
376 return result
378 class PciDeviceParseError(Exception):
379 def __init__(self,msg):
380 self.message = msg
381 def __str__(self):
382 return 'Error Parsing PCI Device Info: '+self.message
384 class PciDeviceAssignmentError(Exception):
385 def __init__(self,msg):
386 self.message = msg
387 def __str__(self):
388 return 'pci: improper device assignment specified: ' + \
389 self.message
391 class PciDeviceVslotMissing(Exception):
392 def __init__(self,msg):
393 self.message = msg
394 def __str__(self):
395 return 'pci: no vslot: ' + self.message
397 class PciDevice:
398 def __init__(self, domain, bus, slot, func):
399 self.domain = domain
400 self.bus = bus
401 self.slot = slot
402 self.func = func
403 self.name = PCI_DEV_FORMAT_STR % (domain, bus, slot, func)
404 self.cfg_space_path = find_sysfs_mnt()+SYSFS_PCI_DEVS_PATH+'/'+ \
405 self.name + SYSFS_PCI_DEV_CONFIG_PATH
406 self.irq = 0
407 self.iomem = []
408 self.ioports = []
409 self.driver = None
410 self.vendor = None
411 self.device = None
412 self.subvendor = None
413 self.subdevice = None
414 self.msix = 0
415 self.msix_iomem = []
416 self.revision = 0
417 self.classcode = None
418 self.vendorname = ""
419 self.devicename = ""
420 self.classname = ""
421 self.subvendorname = ""
422 self.subdevicename = ""
423 self.dev_type = None
424 self.has_non_page_aligned_bar = False
425 self.pcie_flr = False
426 self.pci_af_flr = False
427 self.detect_dev_info()
428 self.get_info_from_sysfs()
429 self.get_info_from_lspci()
431 def find_parent(self):
432 # i.e., /sys/bus/pci/devices/0000:00:19.0 or
433 # /sys/bus/pci/devices/0000:03:04.0
434 path = find_sysfs_mnt()+SYSFS_PCI_DEVS_PATH+'/'+ self.name
435 # i.e., ../../../devices/pci0000:00/0000:00:19.0
436 # ../../../devices/pci0000:00/0000:00:02.0/0000:01:00.2/0000:03:04.0
437 try:
438 target = os.readlink(path)
439 lst = target.split('/')
440 parent = lst[len(lst)-2]
441 if parent[0:3] == 'pci':
442 # We have reached the upmost one.
443 return None
444 else:
445 lst = parent.split(':')
446 dom = int(lst[0], 16)
447 bus = int(lst[1], 16)
448 lst = lst[2]
449 lst = lst.split('.')
450 dev = int(lst[0], 16)
451 func = int(lst[1], 16)
452 return (dom, bus, dev, func)
453 except OSError, (errno, strerr):
454 raise PciDeviceParseError('Can not locate the parent of %s',
455 self.name)
457 def find_the_uppermost_pci_bridge(self):
458 # Find the uppermost PCI/PCI-X bridge
459 dev = self.find_parent()
460 if dev is None:
461 return None
462 (dom, b, d, f) = dev
463 dev = dev_parent = PciDevice(dom, b, d, f)
464 while dev_parent.dev_type != DEV_TYPE_PCIe_BRIDGE:
465 parent = dev_parent.find_parent()
466 if parent is None:
467 break
468 (dom, b, d, f) = parent
469 dev = dev_parent
470 dev_parent = PciDevice(dom, b, d, f)
471 return dev
473 def find_all_devices_behind_the_bridge(self, ignore_bridge):
474 sysfs_mnt = find_sysfs_mnt()
475 self_path = sysfs_mnt + SYSFS_PCI_DEVS_PATH + '/' + self.name
476 pci_names = os.popen('ls ' + self_path).read()
477 dev_list = extract_the_exact_pci_names(pci_names)
479 list = [self.name]
480 for pci_str in dev_list:
481 (dom, b, d, f) = parse_pci_name(pci_str)
482 dev = PciDevice(dom, b, d, f)
483 if dev.dev_type == DEV_TYPE_PCI_BRIDGE or \
484 dev.dev_type == DEV_TYPE_PCIe_BRIDGE:
485 sub_list_including_self = \
486 dev.find_all_devices_behind_the_bridge(ignore_bridge)
487 if ignore_bridge:
488 del sub_list_including_self[0]
489 list = list + [sub_list_including_self]
490 else:
491 list = list + [dev.name]
492 return list
494 def find_coassigned_pci_devices(self, ignore_bridge = True):
495 ''' Here'self' is a PCI device, we need find the uppermost PCI/PCI-X
496 bridge, and all devices behind it must be co-assigned to the same
497 guest.
499 Parameter:
500 [ignore_bridge]: if set, the returned result doesn't include
501 any bridge behind the uppermost PCI/PCI-X bridge.
503 Note: The first element of the return value is the uppermost
504 PCI/PCI-X bridge. If the caller doesn't need the first
505 element, the caller itself can remove it explicitly.
506 '''
507 dev = self.find_the_uppermost_pci_bridge()
509 # The 'self' device is on bus0.
510 if dev is None:
511 return [self.name]
513 dev_list = dev.find_all_devices_behind_the_bridge(ignore_bridge)
514 dev_list = extract_the_exact_pci_names(dev_list)
515 return dev_list
517 def do_secondary_bus_reset(self, target_bus, devs):
518 # Save the config spaces of all the devices behind the bus.
519 (pci_list, cfg_list) = save_pci_conf_space(devs)
521 #Do the Secondary Bus Reset
522 sysfs_mnt = find_sysfs_mnt()
523 parent_path = sysfs_mnt + SYSFS_PCI_DEVS_PATH + '/' + \
524 target_bus + SYSFS_PCI_DEV_CONFIG_PATH
525 fd = os.open(parent_path, os.O_RDWR)
526 os.lseek(fd, PCI_CB_BRIDGE_CONTROL, 0)
527 br_cntl = (struct.unpack('H', os.read(fd, 2)))[0]
528 # Assert Secondary Bus Reset
529 os.lseek(fd, PCI_CB_BRIDGE_CONTROL, 0)
530 br_cntl |= PCI_BRIDGE_CTL_BUS_RESET
531 os.write(fd, struct.pack('H', br_cntl))
532 time.sleep(0.100)
533 # De-assert Secondary Bus Reset
534 os.lseek(fd, PCI_CB_BRIDGE_CONTROL, 0)
535 br_cntl &= ~PCI_BRIDGE_CTL_BUS_RESET
536 os.write(fd, struct.pack('H', br_cntl))
537 time.sleep(0.100)
538 os.close(fd)
540 # Restore the config spaces
541 restore_pci_conf_space((pci_list, cfg_list))
543 def do_Dstate_transition(self):
544 pos = self.find_cap_offset(PCI_CAP_ID_PM)
545 if pos == 0:
546 return False
548 # No_Soft_Reset - When set 1, this bit indicates that
549 # devices transitioning from D3hot to D0 because of
550 # PowerState commands do not perform an internal reset.
551 pm_ctl = self.pci_conf_read32(pos + PCI_PM_CTRL)
552 if (pm_ctl & PCI_PM_CTRL_NO_SOFT_RESET) == 1:
553 return False
555 (pci_list, cfg_list) = save_pci_conf_space([self.name])
557 # Enter D3hot
558 pm_ctl &= ~PCI_PM_CTRL_STATE_MASK
559 pm_ctl |= PCI_D3hot
560 self.pci_conf_write32(pos + PCI_PM_CTRL, pm_ctl)
561 time.sleep(0.010)
563 # From D3hot to D0
564 pm_ctl &= ~PCI_PM_CTRL_STATE_MASK
565 pm_ctl |= PCI_D0hot
566 self.pci_conf_write32(pos + PCI_PM_CTRL, pm_ctl)
567 time.sleep(0.010)
569 restore_pci_conf_space((pci_list, cfg_list))
570 return True
572 def do_vendor_specific_FLR_method(self):
573 pos = self.find_cap_offset(PCI_CAP_ID_VENDOR_SPECIFIC_CAP)
574 if pos == 0:
575 return
577 vendor_id = self.pci_conf_read16(PCI_VENDOR_ID)
578 if vendor_id != VENDOR_INTEL:
579 return
581 class_id = self.pci_conf_read16(PCI_CLASS_DEVICE)
582 if class_id != PCI_CLASS_ID_USB:
583 return
585 (pci_list, cfg_list) = save_pci_conf_space([self.name])
587 self.pci_conf_write8(pos + PCI_USB_FLRCTRL, 1)
588 time.sleep(0.100)
590 restore_pci_conf_space((pci_list, cfg_list))
592 def do_FLR_for_integrated_device(self):
593 if not self.do_Dstate_transition():
594 self.do_vendor_specific_FLR_method()
596 def find_all_the_multi_functions(self):
597 sysfs_mnt = find_sysfs_mnt()
598 parent = PCI_DEV_FORMAT_STR % self.find_parent()
599 pci_names = os.popen('ls ' + sysfs_mnt + SYSFS_PCI_DEVS_PATH + '/' + \
600 parent + '/').read()
601 funcs = extract_the_exact_pci_names(pci_names)
602 return funcs
604 def find_coassigned_devices(self):
605 if self.dev_type == DEV_TYPE_PCIe_ENDPOINT and not self.pcie_flr:
606 return self.find_all_the_multi_functions()
607 elif self.dev_type == DEV_TYPE_PCI and not self.pci_af_flr:
608 coassigned_pci_list = self.find_coassigned_pci_devices(True)
609 if len(coassigned_pci_list) > 1:
610 del coassigned_pci_list[0]
611 return coassigned_pci_list
612 else:
613 return [self.name]
615 def find_cap_offset(self, cap):
616 path = find_sysfs_mnt()+SYSFS_PCI_DEVS_PATH+'/'+ \
617 self.name+SYSFS_PCI_DEV_CONFIG_PATH
619 pos = PCI_CAPABILITY_LIST
621 try:
622 fd = os.open(path, os.O_RDONLY)
623 os.lseek(fd, PCI_STATUS, 0)
624 status = struct.unpack('H', os.read(fd, 2))[0]
625 if (status & 0x10) == 0:
626 # The device doesn't support PCI_STATUS_CAP_LIST
627 return 0
629 max_cap = 48
630 while max_cap > 0:
631 os.lseek(fd, pos, 0)
632 pos = ord(os.read(fd, 1))
633 if pos < 0x40:
634 pos = 0
635 break;
636 os.lseek(fd, pos + 0, 0)
637 id = ord(os.read(fd, 1))
638 if id == 0xff:
639 pos = 0
640 break;
642 # Found the capability
643 if id == cap:
644 break;
646 # Test the next one
647 pos = pos + 1
648 max_cap = max_cap - 1;
650 os.close(fd)
651 except OSError, (errno, strerr):
652 raise PciDeviceParseError(('Error when accessing sysfs: %s (%d)' %
653 (strerr, errno)))
654 return pos
656 def pci_conf_read8(self, pos):
657 fd = os.open(self.cfg_space_path, os.O_RDONLY)
658 os.lseek(fd, pos, 0)
659 str = os.read(fd, 1)
660 os.close(fd)
661 val = struct.unpack('B', str)[0]
662 return val
664 def pci_conf_read16(self, pos):
665 fd = os.open(self.cfg_space_path, os.O_RDONLY)
666 os.lseek(fd, pos, 0)
667 str = os.read(fd, 2)
668 os.close(fd)
669 val = struct.unpack('H', str)[0]
670 return val
672 def pci_conf_read32(self, pos):
673 fd = os.open(self.cfg_space_path, os.O_RDONLY)
674 os.lseek(fd, pos, 0)
675 str = os.read(fd, 4)
676 os.close(fd)
677 val = struct.unpack('I', str)[0]
678 return val
680 def pci_conf_write8(self, pos, val):
681 str = struct.pack('B', val)
682 fd = os.open(self.cfg_space_path, os.O_WRONLY)
683 os.lseek(fd, pos, 0)
684 os.write(fd, str)
685 os.close(fd)
687 def pci_conf_write16(self, pos, val):
688 str = struct.pack('H', val)
689 fd = os.open(self.cfg_space_path, os.O_WRONLY)
690 os.lseek(fd, pos, 0)
691 os.write(fd, str)
692 os.close(fd)
694 def pci_conf_write32(self, pos, val):
695 str = struct.pack('I', val)
696 fd = os.open(self.cfg_space_path, os.O_WRONLY)
697 os.lseek(fd, pos, 0)
698 os.write(fd, str)
699 os.close(fd)
701 def detect_dev_info(self):
702 class_dev = self.pci_conf_read16(PCI_CLASS_DEVICE)
703 pos = self.find_cap_offset(PCI_CAP_ID_EXP)
704 if class_dev == PCI_CLASS_BRIDGE_PCI:
705 if pos == 0:
706 self.dev_type = DEV_TYPE_PCI_BRIDGE
707 else:
708 creg = self.pci_conf_read16(pos + PCI_EXP_FLAGS)
709 if ((creg & PCI_EXP_FLAGS_TYPE) >> 4) == \
710 PCI_EXP_TYPE_PCI_BRIDGE:
711 self.dev_type = DEV_TYPE_PCI_BRIDGE
712 else:
713 self.dev_type = DEV_TYPE_PCIe_BRIDGE
714 else:
715 if pos != 0:
716 self.dev_type = DEV_TYPE_PCIe_ENDPOINT
717 else:
718 self.dev_type = DEV_TYPE_PCI
720 # Force 0000:00:00.0 to be DEV_TYPE_PCIe_BRIDGE
721 if self.name == '0000:00:00.0':
722 self.dev_type = DEV_TYPE_PCIe_BRIDGE
724 if (self.dev_type == DEV_TYPE_PCI_BRIDGE) or \
725 (self.dev_type == DEV_TYPE_PCIe_BRIDGE):
726 return
728 # Try to findthe PCIe FLR capability
729 if self.dev_type == DEV_TYPE_PCIe_ENDPOINT:
730 dev_cap = self.pci_conf_read32(pos + PCI_EXP_DEVCAP)
731 if dev_cap & PCI_EXP_DEVCAP_FLR:
732 self.pcie_flr = True
733 elif self.dev_type == DEV_TYPE_PCI:
734 # Try to find the "PCI Advanced Capabilities"
735 pos = self.find_cap_offset(PCI_CAP_ID_AF)
736 if pos != 0:
737 af_cap = self.pci_conf_read8(pos + PCI_AF_CAPs)
738 if (af_cap & PCI_AF_CAPs_TP_FLR) == PCI_AF_CAPs_TP_FLR:
739 self.pci_af_flr = True
741 bar_addr = PCI_BAR_0
742 while bar_addr <= PCI_BAR_5:
743 bar = self.pci_conf_read32(bar_addr)
744 if (bar & PCI_BAR_SPACE) == PCI_BAR_MEM:
745 bar = bar & PCI_BAR_MEM_MASK
746 bar = bar & ~PAGE_MASK
747 if bar != 0:
748 self.has_non_page_aligned_bar = True
749 break
750 bar_addr = bar_addr + 4
752 def devs_check_driver(self, devs):
753 if len(devs) == 0:
754 return
755 for pci_dev in devs:
756 (dom, b, d, f) = parse_pci_name(pci_dev)
757 dev = PciDevice(dom, b, d, f)
758 if dev.driver == 'pciback':
759 continue
760 err_msg = 'pci: %s must be co-assigned to the same guest with %s' + \
761 ', but it is not owned by pciback.'
762 raise PciDeviceAssignmentError(err_msg % (pci_dev, self.name))
764 def do_FLR(self):
765 """ Perform FLR (Functional Level Reset) for the device.
766 """
767 if self.dev_type == DEV_TYPE_PCIe_ENDPOINT:
768 # If PCIe device supports FLR, we use it.
769 if self.pcie_flr:
770 (pci_list, cfg_list) = save_pci_conf_space([self.name])
771 pos = self.find_cap_offset(PCI_CAP_ID_EXP)
772 self.pci_conf_write32(pos + PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_FLR)
773 # We must sleep at least 100ms for the completion of FLR
774 time.sleep(0.100)
775 restore_pci_conf_space((pci_list, cfg_list))
776 else:
777 if self.bus == 0:
778 self.do_FLR_for_integrated_device()
779 else:
780 funcs = self.find_all_the_multi_functions()
781 self.devs_check_driver(funcs)
783 parent = '%04x:%02x:%02x.%01x' % self.find_parent()
785 # Do Secondary Bus Reset.
786 self.do_secondary_bus_reset(parent, funcs)
787 # PCI devices
788 else:
789 # For PCI device on host bus, we test "PCI Advanced Capabilities".
790 if self.bus == 0 and self.pci_af_flr:
791 (pci_list, cfg_list) = save_pci_conf_space([self.name])
792 # We use Advanced Capability to do FLR.
793 pos = self.find_cap_offset(PCI_CAP_ID_AF)
794 self.pci_conf_write8(pos + PCI_AF_CTL, PCI_AF_CTL_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 devs = self.find_coassigned_pci_devices(False)
802 # Remove the element 0 which is a bridge
803 target_bus = devs[0]
804 del devs[0]
805 self.devs_check_driver(devs)
807 # Do Secondary Bus Reset.
808 self.do_secondary_bus_reset(target_bus, devs)
810 def find_capability(self, type):
811 sysfs_mnt = find_sysfs_mnt()
812 if sysfs_mnt == None:
813 return False
814 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
815 self.name+SYSFS_PCI_DEV_CONFIG_PATH
816 try:
817 conf_file = open(path, 'rb')
818 conf_file.seek(PCI_HEADER_TYPE)
819 header_type = ord(conf_file.read(1)) & PCI_HEADER_TYPE_MASK
820 if header_type == PCI_HEADER_TYPE_CARDBUS:
821 return
822 conf_file.seek(PCI_STATUS_OFFSET)
823 status = ord(conf_file.read(1))
824 if status&PCI_STATUS_CAP_MASK:
825 conf_file.seek(PCI_CAP_OFFSET)
826 capa_pointer = ord(conf_file.read(1))
827 capa_count = 0
828 while capa_pointer:
829 if capa_pointer < 0x40:
830 raise PciDeviceParseError(
831 ('Broken capability chain: %s' % self.name))
832 capa_count += 1
833 if capa_count > 96:
834 raise PciDeviceParseError(
835 ('Looped capability chain: %s' % self.name))
836 conf_file.seek(capa_pointer)
837 capa_id = ord(conf_file.read(1))
838 capa_pointer = ord(conf_file.read(1))
839 if capa_id == type:
840 # get the type
841 message_cont_lo = ord(conf_file.read(1))
842 message_cont_hi = ord(conf_file.read(1))
843 self.msix=1
844 self.msix_entries = (message_cont_lo + \
845 (message_cont_hi << 8)) \
846 & MSIX_SIZE_MASK
847 t_off=conf_file.read(4)
848 p_off=conf_file.read(4)
849 self.table_offset=ord(t_off[0]) | (ord(t_off[1])<<8) | \
850 (ord(t_off[2])<<16)| \
851 (ord(t_off[3])<<24)
852 self.pba_offset=ord(p_off[0]) | (ord(p_off[1]) << 8)| \
853 (ord(p_off[2])<<16) | \
854 (ord(p_off[3])<<24)
855 self.table_index = self.table_offset & MSIX_BIR_MASK
856 self.table_offset = self.table_offset & ~MSIX_BIR_MASK
857 self.pba_index = self.pba_offset & MSIX_BIR_MASK
858 self.pba_offset = self.pba_offset & ~MSIX_BIR_MASK
859 break
860 except IOError, (errno, strerr):
861 raise PciDeviceParseError(('Failed to locate sysfs mount: %s: %s (%d)' %
862 (PROC_PCI_PATH, strerr, errno)))
864 def remove_msix_iomem(self, index, start, size):
865 if (index == self.table_index):
866 table_start = start+self.table_offset
867 table_end = table_start + self.msix_entries * 16
868 table_start = table_start & PAGE_MASK
869 table_end = (table_end + PAGE_SIZE) & PAGE_MASK
870 self.msix_iomem.append((table_start, table_end-table_start))
871 if (index==self.pba_index):
872 pba_start = start + self.pba_offset
873 pba_end = pba_start + self.msix_entries/8
874 pba_start = pba_start & PAGE_MASK
875 pba_end = (pba_end + PAGE_SIZE) & PAGE_MASK
876 self.msix_iomem.append((pba_start, pba_end-pba_start))
878 def get_info_from_sysfs(self):
879 self.find_capability(0x11)
880 sysfs_mnt = find_sysfs_mnt()
881 if sysfs_mnt == None:
882 return False
884 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
885 self.name+SYSFS_PCI_DEV_RESOURCE_PATH
886 try:
887 resource_file = open(path,'r')
889 for i in range(PROC_PCI_NUM_RESOURCES):
890 line = resource_file.readline()
891 sline = line.split()
892 if len(sline)<3:
893 continue
895 start = int(sline[0],16)
896 end = int(sline[1],16)
897 flags = int(sline[2],16)
898 size = end-start+1
900 if start!=0:
901 if flags&PCI_BAR_IO:
902 self.ioports.append( (start,size) )
903 else:
904 self.iomem.append( (start,size) )
905 if (self.msix):
906 self.remove_msix_iomem(i, start, size)
910 except IOError, (errno, strerr):
911 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
912 (path, strerr, errno)))
914 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
915 self.name+SYSFS_PCI_DEV_IRQ_PATH
916 try:
917 self.irq = int(open(path,'r').readline())
918 except IOError, (errno, strerr):
919 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
920 (path, strerr, errno)))
922 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
923 self.name+SYSFS_PCI_DEV_DRIVER_DIR_PATH
924 try:
925 self.driver = os.path.basename(os.readlink(path))
926 except OSError, (errno, strerr):
927 self.driver = ""
929 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
930 self.name+SYSFS_PCI_DEV_VENDOR_PATH
931 try:
932 self.vendor = int(open(path,'r').readline(), 16)
933 except IOError, (errno, strerr):
934 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
935 (path, strerr, errno)))
937 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
938 self.name+SYSFS_PCI_DEV_DEVICE_PATH
939 try:
940 self.device = int(open(path,'r').readline(), 16)
941 except IOError, (errno, strerr):
942 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
943 (path, strerr, errno)))
945 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
946 self.name+SYSFS_PCI_DEV_SUBVENDOR_PATH
947 try:
948 self.subvendor = int(open(path,'r').readline(), 16)
949 except IOError, (errno, strerr):
950 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
951 (path, strerr, errno)))
953 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
954 self.name+SYSFS_PCI_DEV_SUBDEVICE_PATH
955 try:
956 self.subdevice = int(open(path,'r').readline(), 16)
957 except IOError, (errno, strerr):
958 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
959 (path, strerr, errno)))
961 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
962 self.name+SYSFS_PCI_DEV_CLASS_PATH
963 try:
964 self.classcode = int(open(path,'r').readline(), 16)
965 except IOError, (errno, strerr):
966 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
967 (path, strerr, errno)))
969 return True
971 def get_info_from_lspci(self):
972 """ Get information such as vendor name, device name, class name, etc.
973 Since we cannot obtain these data from sysfs, use 'lspci' command.
974 """
975 global lspci_info
976 global lspci_info_lock
978 lspci_info_lock.acquire()
979 try:
980 if lspci_info is None:
981 _create_lspci_info()
983 try:
984 device_info = lspci_info[self.name]
985 self.revision = int(device_info['Rev'], 16)
986 self.vendorname = device_info['Vendor']
987 self.devicename = device_info['Device']
988 self.classname = device_info['Class']
989 self.subvendorname = device_info['SVendor']
990 self.subdevicename = device_info['SDevice']
991 except KeyError:
992 pass
994 return True
995 finally:
996 lspci_info_lock.release()
998 def __str__(self):
999 str = "PCI Device %s\n" % (self.name)
1000 for (start,size) in self.ioports:
1001 str = str + "IO Port 0x%02x [size=%d]\n"%(start,size)
1002 for (start,size) in self.iomem:
1003 str = str + "IO Mem 0x%02x [size=%d]\n"%(start,size)
1004 str = str + "IRQ %d\n"%(self.irq)
1005 str = str + "Vendor ID 0x%04x\n"%(self.vendor)
1006 str = str + "Device ID 0x%04x\n"%(self.device)
1007 str = str + "Sybsystem Vendor ID 0x%04x\n"%(self.subvendor)
1008 str = str + "Subsystem Device ID 0x%04x"%(self.subdevice)
1009 return str
1011 def main():
1012 if len(sys.argv)<5:
1013 print "Usage: %s <domain> <bus> <slot> <func>\n" % sys.argv[0]
1014 sys.exit(2)
1016 dev = PciDevice(int(sys.argv[1],16), int(sys.argv[2],16),
1017 int(sys.argv[3],16), int(sys.argv[4],16))
1018 print str(dev)
1020 if __name__=='__main__':
1021 main()