ia64/xen-unstable

view tools/python/xen/util/pci.py @ 19758:0573bbe19499

xend: pass-through: sxp.merge() cant deal with values being a list

sxp.merge() can't deal with values being a list so instead
of storing pci options as:

[ 'opts', [ 'key1' 'value1'], [ 'key2', 'value2'], ...]

store them as:

[ 'opts', [ 'key1' 'value1'], ['opts', [ 'key2', 'value2']], ...

Signed-off-by: Simon Horman <horms@verge.net.au>
author Keir Fraser <keir.fraser@citrix.com>
date Tue Jun 16 11:37:41 2009 +0100 (2009-06-16)
parents 08de8ec655c2
children 11c3f4e786b3
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 PciDeviceNotFoundError(Exception):
379 def __init__(self,domain,bus,slot,func):
380 self.domain = domain
381 self.bus = bus
382 self.slot = slot
383 self.func = func
384 self.name = PCI_DEV_FORMAT_STR %(domain, bus, slot, func)
386 def __str__(self):
387 return ('PCI Device %s Not Found' % (self.name))
389 class PciDeviceParseError(Exception):
390 def __init__(self,msg):
391 self.message = msg
392 def __str__(self):
393 return 'Error Parsing PCI Device Info: '+self.message
395 class PciDeviceAssignmentError(Exception):
396 def __init__(self,msg):
397 self.message = msg
398 def __str__(self):
399 return 'pci: improper device assignment specified: ' + \
400 self.message
402 class PciDeviceVslotMissing(Exception):
403 def __init__(self,msg):
404 self.message = msg
405 def __str__(self):
406 return 'pci: no vslot: ' + self.message
408 class PciDevice:
409 def __init__(self, domain, bus, slot, func):
410 self.domain = domain
411 self.bus = bus
412 self.slot = slot
413 self.func = func
414 self.name = PCI_DEV_FORMAT_STR % (domain, bus, slot, func)
415 self.cfg_space_path = find_sysfs_mnt()+SYSFS_PCI_DEVS_PATH+'/'+ \
416 self.name + SYSFS_PCI_DEV_CONFIG_PATH
417 self.irq = 0
418 self.iomem = []
419 self.ioports = []
420 self.driver = None
421 self.vendor = None
422 self.device = None
423 self.subvendor = None
424 self.subdevice = None
425 self.msix = 0
426 self.msix_iomem = []
427 self.revision = 0
428 self.classcode = None
429 self.vendorname = ""
430 self.devicename = ""
431 self.classname = ""
432 self.subvendorname = ""
433 self.subdevicename = ""
434 self.dev_type = None
435 self.has_non_page_aligned_bar = False
436 self.pcie_flr = False
437 self.pci_af_flr = False
438 self.detect_dev_info()
439 self.get_info_from_sysfs()
440 self.get_info_from_lspci()
442 def find_parent(self):
443 # i.e., /sys/bus/pci/devices/0000:00:19.0 or
444 # /sys/bus/pci/devices/0000:03:04.0
445 path = find_sysfs_mnt()+SYSFS_PCI_DEVS_PATH+'/'+ self.name
446 # i.e., ../../../devices/pci0000:00/0000:00:19.0
447 # ../../../devices/pci0000:00/0000:00:02.0/0000:01:00.2/0000:03:04.0
448 try:
449 target = os.readlink(path)
450 lst = target.split('/')
451 parent = lst[len(lst)-2]
452 if parent[0:3] == 'pci':
453 # We have reached the upmost one.
454 return None
455 else:
456 lst = parent.split(':')
457 dom = int(lst[0], 16)
458 bus = int(lst[1], 16)
459 lst = lst[2]
460 lst = lst.split('.')
461 dev = int(lst[0], 16)
462 func = int(lst[1], 16)
463 return (dom, bus, dev, func)
464 except OSError, (errno, strerr):
465 raise PciDeviceParseError('Can not locate the parent of %s',
466 self.name)
468 def find_the_uppermost_pci_bridge(self):
469 # Find the uppermost PCI/PCI-X bridge
470 dev = self.find_parent()
471 if dev is None:
472 return None
473 (dom, b, d, f) = dev
474 dev = dev_parent = PciDevice(dom, b, d, f)
475 while dev_parent.dev_type != DEV_TYPE_PCIe_BRIDGE:
476 parent = dev_parent.find_parent()
477 if parent is None:
478 break
479 (dom, b, d, f) = parent
480 dev = dev_parent
481 dev_parent = PciDevice(dom, b, d, f)
482 return dev
484 def find_all_devices_behind_the_bridge(self, ignore_bridge):
485 sysfs_mnt = find_sysfs_mnt()
486 self_path = sysfs_mnt + SYSFS_PCI_DEVS_PATH + '/' + self.name
487 pci_names = os.popen('ls ' + self_path).read()
488 dev_list = extract_the_exact_pci_names(pci_names)
490 list = [self.name]
491 for pci_str in dev_list:
492 (dom, b, d, f) = parse_pci_name(pci_str)
493 dev = PciDevice(dom, b, d, f)
494 if dev.dev_type == DEV_TYPE_PCI_BRIDGE or \
495 dev.dev_type == DEV_TYPE_PCIe_BRIDGE:
496 sub_list_including_self = \
497 dev.find_all_devices_behind_the_bridge(ignore_bridge)
498 if ignore_bridge:
499 del sub_list_including_self[0]
500 list = list + [sub_list_including_self]
501 else:
502 list = list + [dev.name]
503 return list
505 def find_coassigned_pci_devices(self, ignore_bridge = True):
506 ''' Here'self' is a PCI device, we need find the uppermost PCI/PCI-X
507 bridge, and all devices behind it must be co-assigned to the same
508 guest.
510 Parameter:
511 [ignore_bridge]: if set, the returned result doesn't include
512 any bridge behind the uppermost PCI/PCI-X bridge.
514 Note: The first element of the return value is the uppermost
515 PCI/PCI-X bridge. If the caller doesn't need the first
516 element, the caller itself can remove it explicitly.
517 '''
518 dev = self.find_the_uppermost_pci_bridge()
520 # The 'self' device is on bus0.
521 if dev is None:
522 return [self.name]
524 dev_list = dev.find_all_devices_behind_the_bridge(ignore_bridge)
525 dev_list = extract_the_exact_pci_names(dev_list)
526 return dev_list
528 def do_secondary_bus_reset(self, target_bus, devs):
529 # Save the config spaces of all the devices behind the bus.
530 (pci_list, cfg_list) = save_pci_conf_space(devs)
532 #Do the Secondary Bus Reset
533 sysfs_mnt = find_sysfs_mnt()
534 parent_path = sysfs_mnt + SYSFS_PCI_DEVS_PATH + '/' + \
535 target_bus + SYSFS_PCI_DEV_CONFIG_PATH
536 fd = os.open(parent_path, os.O_RDWR)
537 os.lseek(fd, PCI_CB_BRIDGE_CONTROL, 0)
538 br_cntl = (struct.unpack('H', os.read(fd, 2)))[0]
539 # Assert Secondary Bus Reset
540 os.lseek(fd, PCI_CB_BRIDGE_CONTROL, 0)
541 br_cntl |= PCI_BRIDGE_CTL_BUS_RESET
542 os.write(fd, struct.pack('H', br_cntl))
543 time.sleep(0.100)
544 # De-assert Secondary Bus Reset
545 os.lseek(fd, PCI_CB_BRIDGE_CONTROL, 0)
546 br_cntl &= ~PCI_BRIDGE_CTL_BUS_RESET
547 os.write(fd, struct.pack('H', br_cntl))
548 time.sleep(0.100)
549 os.close(fd)
551 # Restore the config spaces
552 restore_pci_conf_space((pci_list, cfg_list))
554 def do_Dstate_transition(self):
555 pos = self.find_cap_offset(PCI_CAP_ID_PM)
556 if pos == 0:
557 return False
559 # No_Soft_Reset - When set 1, this bit indicates that
560 # devices transitioning from D3hot to D0 because of
561 # PowerState commands do not perform an internal reset.
562 pm_ctl = self.pci_conf_read32(pos + PCI_PM_CTRL)
563 if (pm_ctl & PCI_PM_CTRL_NO_SOFT_RESET) == 1:
564 return False
566 (pci_list, cfg_list) = save_pci_conf_space([self.name])
568 # Enter D3hot
569 pm_ctl &= ~PCI_PM_CTRL_STATE_MASK
570 pm_ctl |= PCI_D3hot
571 self.pci_conf_write32(pos + PCI_PM_CTRL, pm_ctl)
572 time.sleep(0.010)
574 # From D3hot to D0
575 pm_ctl &= ~PCI_PM_CTRL_STATE_MASK
576 pm_ctl |= PCI_D0hot
577 self.pci_conf_write32(pos + PCI_PM_CTRL, pm_ctl)
578 time.sleep(0.010)
580 restore_pci_conf_space((pci_list, cfg_list))
581 return True
583 def do_vendor_specific_FLR_method(self):
584 pos = self.find_cap_offset(PCI_CAP_ID_VENDOR_SPECIFIC_CAP)
585 if pos == 0:
586 return
588 vendor_id = self.pci_conf_read16(PCI_VENDOR_ID)
589 if vendor_id != VENDOR_INTEL:
590 return
592 class_id = self.pci_conf_read16(PCI_CLASS_DEVICE)
593 if class_id != PCI_CLASS_ID_USB:
594 return
596 (pci_list, cfg_list) = save_pci_conf_space([self.name])
598 self.pci_conf_write8(pos + PCI_USB_FLRCTRL, 1)
599 time.sleep(0.100)
601 restore_pci_conf_space((pci_list, cfg_list))
603 def do_FLR_for_integrated_device(self):
604 if not self.do_Dstate_transition():
605 self.do_vendor_specific_FLR_method()
607 def find_all_the_multi_functions(self):
608 sysfs_mnt = find_sysfs_mnt()
609 parent = PCI_DEV_FORMAT_STR % self.find_parent()
610 pci_names = os.popen('ls ' + sysfs_mnt + SYSFS_PCI_DEVS_PATH + '/' + \
611 parent + '/').read()
612 funcs = extract_the_exact_pci_names(pci_names)
613 return funcs
615 def find_coassigned_devices(self):
616 if self.dev_type == DEV_TYPE_PCIe_ENDPOINT and not self.pcie_flr:
617 return self.find_all_the_multi_functions()
618 elif self.dev_type == DEV_TYPE_PCI and not self.pci_af_flr:
619 coassigned_pci_list = self.find_coassigned_pci_devices(True)
620 if len(coassigned_pci_list) > 1:
621 del coassigned_pci_list[0]
622 return coassigned_pci_list
623 else:
624 return [self.name]
626 def find_cap_offset(self, cap):
627 path = find_sysfs_mnt()+SYSFS_PCI_DEVS_PATH+'/'+ \
628 self.name+SYSFS_PCI_DEV_CONFIG_PATH
630 pos = PCI_CAPABILITY_LIST
632 try:
633 fd = os.open(path, os.O_RDONLY)
634 os.lseek(fd, PCI_STATUS, 0)
635 status = struct.unpack('H', os.read(fd, 2))[0]
636 if (status & 0x10) == 0:
637 # The device doesn't support PCI_STATUS_CAP_LIST
638 return 0
640 max_cap = 48
641 while max_cap > 0:
642 os.lseek(fd, pos, 0)
643 pos = ord(os.read(fd, 1))
644 if pos < 0x40:
645 pos = 0
646 break;
647 os.lseek(fd, pos + 0, 0)
648 id = ord(os.read(fd, 1))
649 if id == 0xff:
650 pos = 0
651 break;
653 # Found the capability
654 if id == cap:
655 break;
657 # Test the next one
658 pos = pos + 1
659 max_cap = max_cap - 1;
661 os.close(fd)
662 except OSError, (errno, strerr):
663 raise PciDeviceParseError(('Error when accessing sysfs: %s (%d)' %
664 (strerr, errno)))
665 return pos
667 def pci_conf_read8(self, pos):
668 fd = os.open(self.cfg_space_path, os.O_RDONLY)
669 os.lseek(fd, pos, 0)
670 str = os.read(fd, 1)
671 os.close(fd)
672 val = struct.unpack('B', str)[0]
673 return val
675 def pci_conf_read16(self, pos):
676 fd = os.open(self.cfg_space_path, os.O_RDONLY)
677 os.lseek(fd, pos, 0)
678 str = os.read(fd, 2)
679 os.close(fd)
680 val = struct.unpack('H', str)[0]
681 return val
683 def pci_conf_read32(self, pos):
684 fd = os.open(self.cfg_space_path, os.O_RDONLY)
685 os.lseek(fd, pos, 0)
686 str = os.read(fd, 4)
687 os.close(fd)
688 val = struct.unpack('I', str)[0]
689 return val
691 def pci_conf_write8(self, pos, val):
692 str = struct.pack('B', val)
693 fd = os.open(self.cfg_space_path, os.O_WRONLY)
694 os.lseek(fd, pos, 0)
695 os.write(fd, str)
696 os.close(fd)
698 def pci_conf_write16(self, pos, val):
699 str = struct.pack('H', val)
700 fd = os.open(self.cfg_space_path, os.O_WRONLY)
701 os.lseek(fd, pos, 0)
702 os.write(fd, str)
703 os.close(fd)
705 def pci_conf_write32(self, pos, val):
706 str = struct.pack('I', val)
707 fd = os.open(self.cfg_space_path, os.O_WRONLY)
708 os.lseek(fd, pos, 0)
709 os.write(fd, str)
710 os.close(fd)
712 def detect_dev_info(self):
713 class_dev = self.pci_conf_read16(PCI_CLASS_DEVICE)
714 pos = self.find_cap_offset(PCI_CAP_ID_EXP)
715 if class_dev == PCI_CLASS_BRIDGE_PCI:
716 if pos == 0:
717 self.dev_type = DEV_TYPE_PCI_BRIDGE
718 else:
719 creg = self.pci_conf_read16(pos + PCI_EXP_FLAGS)
720 if ((creg & PCI_EXP_FLAGS_TYPE) >> 4) == \
721 PCI_EXP_TYPE_PCI_BRIDGE:
722 self.dev_type = DEV_TYPE_PCI_BRIDGE
723 else:
724 self.dev_type = DEV_TYPE_PCIe_BRIDGE
725 else:
726 if pos != 0:
727 self.dev_type = DEV_TYPE_PCIe_ENDPOINT
728 else:
729 self.dev_type = DEV_TYPE_PCI
731 # Force 0000:00:00.0 to be DEV_TYPE_PCIe_BRIDGE
732 if self.name == '0000:00:00.0':
733 self.dev_type = DEV_TYPE_PCIe_BRIDGE
735 if (self.dev_type == DEV_TYPE_PCI_BRIDGE) or \
736 (self.dev_type == DEV_TYPE_PCIe_BRIDGE):
737 return
739 # Try to findthe PCIe FLR capability
740 if self.dev_type == DEV_TYPE_PCIe_ENDPOINT:
741 dev_cap = self.pci_conf_read32(pos + PCI_EXP_DEVCAP)
742 if dev_cap & PCI_EXP_DEVCAP_FLR:
743 self.pcie_flr = True
744 elif self.dev_type == DEV_TYPE_PCI:
745 # Try to find the "PCI Advanced Capabilities"
746 pos = self.find_cap_offset(PCI_CAP_ID_AF)
747 if pos != 0:
748 af_cap = self.pci_conf_read8(pos + PCI_AF_CAPs)
749 if (af_cap & PCI_AF_CAPs_TP_FLR) == PCI_AF_CAPs_TP_FLR:
750 self.pci_af_flr = True
752 bar_addr = PCI_BAR_0
753 while bar_addr <= PCI_BAR_5:
754 bar = self.pci_conf_read32(bar_addr)
755 if (bar & PCI_BAR_SPACE) == PCI_BAR_MEM:
756 bar = bar & PCI_BAR_MEM_MASK
757 bar = bar & ~PAGE_MASK
758 if bar != 0:
759 self.has_non_page_aligned_bar = True
760 break
761 bar_addr = bar_addr + 4
763 def devs_check_driver(self, devs):
764 if len(devs) == 0:
765 return
766 for pci_dev in devs:
767 (dom, b, d, f) = parse_pci_name(pci_dev)
768 dev = PciDevice(dom, b, d, f)
769 if dev.driver == 'pciback':
770 continue
771 err_msg = 'pci: %s must be co-assigned to the same guest with %s' + \
772 ', but it is not owned by pciback.'
773 raise PciDeviceAssignmentError(err_msg % (pci_dev, self.name))
775 def do_FLR(self):
776 """ Perform FLR (Functional Level Reset) for the device.
777 """
778 if self.dev_type == DEV_TYPE_PCIe_ENDPOINT:
779 # If PCIe device supports FLR, we use it.
780 if self.pcie_flr:
781 (pci_list, cfg_list) = save_pci_conf_space([self.name])
782 pos = self.find_cap_offset(PCI_CAP_ID_EXP)
783 self.pci_conf_write32(pos + PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_FLR)
784 # We must sleep at least 100ms for the completion of FLR
785 time.sleep(0.100)
786 restore_pci_conf_space((pci_list, cfg_list))
787 else:
788 if self.bus == 0:
789 self.do_FLR_for_integrated_device()
790 else:
791 funcs = self.find_all_the_multi_functions()
792 self.devs_check_driver(funcs)
794 parent = '%04x:%02x:%02x.%01x' % self.find_parent()
796 # Do Secondary Bus Reset.
797 self.do_secondary_bus_reset(parent, funcs)
798 # PCI devices
799 else:
800 # For PCI device on host bus, we test "PCI Advanced Capabilities".
801 if self.bus == 0 and self.pci_af_flr:
802 (pci_list, cfg_list) = save_pci_conf_space([self.name])
803 # We use Advanced Capability to do FLR.
804 pos = self.find_cap_offset(PCI_CAP_ID_AF)
805 self.pci_conf_write8(pos + PCI_AF_CTL, PCI_AF_CTL_FLR)
806 time.sleep(0.100)
807 restore_pci_conf_space((pci_list, cfg_list))
808 else:
809 if self.bus == 0:
810 self.do_FLR_for_integrated_device()
811 else:
812 devs = self.find_coassigned_pci_devices(False)
813 # Remove the element 0 which is a bridge
814 target_bus = devs[0]
815 del devs[0]
816 self.devs_check_driver(devs)
818 # Do Secondary Bus Reset.
819 self.do_secondary_bus_reset(target_bus, devs)
821 def find_capability(self, type):
822 sysfs_mnt = find_sysfs_mnt()
823 if sysfs_mnt == None:
824 return False
825 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
826 self.name+SYSFS_PCI_DEV_CONFIG_PATH
827 try:
828 conf_file = open(path, 'rb')
829 conf_file.seek(PCI_HEADER_TYPE)
830 header_type = ord(conf_file.read(1)) & PCI_HEADER_TYPE_MASK
831 if header_type == PCI_HEADER_TYPE_CARDBUS:
832 return
833 conf_file.seek(PCI_STATUS_OFFSET)
834 status = ord(conf_file.read(1))
835 if status&PCI_STATUS_CAP_MASK:
836 conf_file.seek(PCI_CAP_OFFSET)
837 capa_pointer = ord(conf_file.read(1))
838 capa_count = 0
839 while capa_pointer:
840 if capa_pointer < 0x40:
841 raise PciDeviceParseError(
842 ('Broken capability chain: %s' % self.name))
843 capa_count += 1
844 if capa_count > 96:
845 raise PciDeviceParseError(
846 ('Looped capability chain: %s' % self.name))
847 conf_file.seek(capa_pointer)
848 capa_id = ord(conf_file.read(1))
849 capa_pointer = ord(conf_file.read(1))
850 if capa_id == type:
851 # get the type
852 message_cont_lo = ord(conf_file.read(1))
853 message_cont_hi = ord(conf_file.read(1))
854 self.msix=1
855 self.msix_entries = (message_cont_lo + \
856 (message_cont_hi << 8)) \
857 & MSIX_SIZE_MASK
858 t_off=conf_file.read(4)
859 p_off=conf_file.read(4)
860 self.table_offset=ord(t_off[0]) | (ord(t_off[1])<<8) | \
861 (ord(t_off[2])<<16)| \
862 (ord(t_off[3])<<24)
863 self.pba_offset=ord(p_off[0]) | (ord(p_off[1]) << 8)| \
864 (ord(p_off[2])<<16) | \
865 (ord(p_off[3])<<24)
866 self.table_index = self.table_offset & MSIX_BIR_MASK
867 self.table_offset = self.table_offset & ~MSIX_BIR_MASK
868 self.pba_index = self.pba_offset & MSIX_BIR_MASK
869 self.pba_offset = self.pba_offset & ~MSIX_BIR_MASK
870 break
871 except IOError, (errno, strerr):
872 raise PciDeviceParseError(('Failed to locate sysfs mount: %s: %s (%d)' %
873 (PROC_PCI_PATH, strerr, errno)))
875 def remove_msix_iomem(self, index, start, size):
876 if (index == self.table_index):
877 table_start = start+self.table_offset
878 table_end = table_start + self.msix_entries * 16
879 table_start = table_start & PAGE_MASK
880 table_end = (table_end + PAGE_SIZE) & PAGE_MASK
881 self.msix_iomem.append((table_start, table_end-table_start))
882 if (index==self.pba_index):
883 pba_start = start + self.pba_offset
884 pba_end = pba_start + self.msix_entries/8
885 pba_start = pba_start & PAGE_MASK
886 pba_end = (pba_end + PAGE_SIZE) & PAGE_MASK
887 self.msix_iomem.append((pba_start, pba_end-pba_start))
889 def get_info_from_sysfs(self):
890 self.find_capability(0x11)
891 sysfs_mnt = find_sysfs_mnt()
892 if sysfs_mnt == None:
893 return False
895 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
896 self.name+SYSFS_PCI_DEV_RESOURCE_PATH
897 try:
898 resource_file = open(path,'r')
900 for i in range(PROC_PCI_NUM_RESOURCES):
901 line = resource_file.readline()
902 sline = line.split()
903 if len(sline)<3:
904 continue
906 start = int(sline[0],16)
907 end = int(sline[1],16)
908 flags = int(sline[2],16)
909 size = end-start+1
911 if start!=0:
912 if flags&PCI_BAR_IO:
913 self.ioports.append( (start,size) )
914 else:
915 self.iomem.append( (start,size) )
916 if (self.msix):
917 self.remove_msix_iomem(i, start, size)
921 except IOError, (errno, strerr):
922 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
923 (path, strerr, errno)))
925 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
926 self.name+SYSFS_PCI_DEV_IRQ_PATH
927 try:
928 self.irq = int(open(path,'r').readline())
929 except IOError, (errno, strerr):
930 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
931 (path, strerr, errno)))
933 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
934 self.name+SYSFS_PCI_DEV_DRIVER_DIR_PATH
935 try:
936 self.driver = os.path.basename(os.readlink(path))
937 except OSError, (errno, strerr):
938 self.driver = ""
940 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
941 self.name+SYSFS_PCI_DEV_VENDOR_PATH
942 try:
943 self.vendor = int(open(path,'r').readline(), 16)
944 except IOError, (errno, strerr):
945 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
946 (path, strerr, errno)))
948 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
949 self.name+SYSFS_PCI_DEV_DEVICE_PATH
950 try:
951 self.device = int(open(path,'r').readline(), 16)
952 except IOError, (errno, strerr):
953 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
954 (path, strerr, errno)))
956 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
957 self.name+SYSFS_PCI_DEV_SUBVENDOR_PATH
958 try:
959 self.subvendor = int(open(path,'r').readline(), 16)
960 except IOError, (errno, strerr):
961 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
962 (path, strerr, errno)))
964 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
965 self.name+SYSFS_PCI_DEV_SUBDEVICE_PATH
966 try:
967 self.subdevice = int(open(path,'r').readline(), 16)
968 except IOError, (errno, strerr):
969 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
970 (path, strerr, errno)))
972 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
973 self.name+SYSFS_PCI_DEV_CLASS_PATH
974 try:
975 self.classcode = int(open(path,'r').readline(), 16)
976 except IOError, (errno, strerr):
977 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
978 (path, strerr, errno)))
980 return True
982 def get_info_from_lspci(self):
983 """ Get information such as vendor name, device name, class name, etc.
984 Since we cannot obtain these data from sysfs, use 'lspci' command.
985 """
986 global lspci_info
987 global lspci_info_lock
989 lspci_info_lock.acquire()
990 try:
991 if lspci_info is None:
992 _create_lspci_info()
994 try:
995 device_info = lspci_info[self.name]
996 self.revision = int(device_info['Rev'], 16)
997 self.vendorname = device_info['Vendor']
998 self.devicename = device_info['Device']
999 self.classname = device_info['Class']
1000 self.subvendorname = device_info['SVendor']
1001 self.subdevicename = device_info['SDevice']
1002 except KeyError:
1003 pass
1005 return True
1006 finally:
1007 lspci_info_lock.release()
1009 def __str__(self):
1010 str = "PCI Device %s\n" % (self.name)
1011 for (start,size) in self.ioports:
1012 str = str + "IO Port 0x%02x [size=%d]\n"%(start,size)
1013 for (start,size) in self.iomem:
1014 str = str + "IO Mem 0x%02x [size=%d]\n"%(start,size)
1015 str = str + "IRQ %d\n"%(self.irq)
1016 str = str + "Vendor ID 0x%04x\n"%(self.vendor)
1017 str = str + "Device ID 0x%04x\n"%(self.device)
1018 str = str + "Sybsystem Vendor ID 0x%04x\n"%(self.subvendor)
1019 str = str + "Subsystem Device ID 0x%04x"%(self.subdevice)
1020 return str
1022 def main():
1023 if len(sys.argv)<5:
1024 print "Usage: %s <domain> <bus> <slot> <func>\n" % sys.argv[0]
1025 sys.exit(2)
1027 dev = PciDevice(int(sys.argv[1],16), int(sys.argv[2],16),
1028 int(sys.argv[3],16), int(sys.argv[4],16))
1029 print str(dev)
1031 if __name__=='__main__':
1032 main()