ia64/xen-unstable

view tools/python/xen/util/pci.py @ 19755:08de8ec655c2

xend: pass-through: fix typo: spx -> sxp

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