debuggers.hg

view tools/python/xen/util/pci.py @ 19680:dc7de36c94e3

xm, xend: passthrough: Add assigned_or_requested_vslot()

Add an accessor to simplify accessing vslot if available,
otherwise requested_vslot.

Signed-off-by: Simon Horman <horms@verge.net.au>
author Keir Fraser <keir.fraser@citrix.com>
date Tue May 26 10:04:10 2009 +0100 (2009-05-26)
parents b183684130fd
children e95c4611a0ae
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
18 PROC_PCI_PATH = '/proc/bus/pci/devices'
19 PROC_PCI_NUM_RESOURCES = 7
21 SYSFS_PCI_DEVS_PATH = '/bus/pci/devices'
22 SYSFS_PCI_DEV_RESOURCE_PATH = '/resource'
23 SYSFS_PCI_DEV_CONFIG_PATH = '/config'
24 SYSFS_PCI_DEV_IRQ_PATH = '/irq'
25 SYSFS_PCI_DEV_DRIVER_DIR_PATH = '/driver'
26 SYSFS_PCI_DEV_VENDOR_PATH = '/vendor'
27 SYSFS_PCI_DEV_DEVICE_PATH = '/device'
28 SYSFS_PCI_DEV_SUBVENDOR_PATH = '/subsystem_vendor'
29 SYSFS_PCI_DEV_SUBDEVICE_PATH = '/subsystem_device'
30 SYSFS_PCI_DEV_CLASS_PATH = '/class'
31 SYSFS_PCIBACK_PATH = '/bus/pci/drivers/pciback/'
33 LSPCI_CMD = 'lspci'
35 PCI_DEV_REG_EXPRESS_STR = r"[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}."+ \
36 r"[0-9a-fA-F]{1}"
37 PCI_DEV_FORMAT_STR = '%04x:%02x:%02x.%01x'
39 DEV_TYPE_PCIe_ENDPOINT = 0
40 DEV_TYPE_PCIe_BRIDGE = 1
41 DEV_TYPE_PCI_BRIDGE = 2
42 DEV_TYPE_PCI = 3
44 PCI_VENDOR_ID = 0x0
45 PCI_STATUS = 0x6
46 PCI_CLASS_DEVICE = 0x0a
47 PCI_CLASS_BRIDGE_PCI = 0x0604
49 PCI_HEADER_TYPE = 0x0e
50 PCI_HEADER_TYPE_MASK = 0x7f
51 PCI_HEADER_TYPE_NORMAL = 0
52 PCI_HEADER_TYPE_BRIDGE = 1
53 PCI_HEADER_TYPE_CARDBUS = 2
55 PCI_CAPABILITY_LIST = 0x34
56 PCI_CB_BRIDGE_CONTROL = 0x3e
57 PCI_BRIDGE_CTL_BUS_RESET= 0x40
59 PCI_CAP_ID_EXP = 0x10
60 PCI_EXP_FLAGS = 0x2
61 PCI_EXP_FLAGS_TYPE = 0x00f0
62 PCI_EXP_TYPE_PCI_BRIDGE = 0x7
63 PCI_EXP_DEVCAP = 0x4
64 PCI_EXP_DEVCAP_FLR = (0x1 << 28)
65 PCI_EXP_DEVCTL = 0x8
66 PCI_EXP_DEVCTL_FLR = (0x1 << 15)
68 PCI_CAP_ID_PM = 0x01
69 PCI_PM_CTRL = 4
70 PCI_PM_CTRL_NO_SOFT_RESET = 0x0008
71 PCI_PM_CTRL_STATE_MASK = 0x0003
72 PCI_D3hot = 3
73 PCI_D0hot = 0
75 VENDOR_INTEL = 0x8086
76 PCI_CAP_ID_VENDOR_SPECIFIC_CAP = 0x09
77 PCI_CLASS_ID_USB = 0x0c03
78 PCI_USB_FLRCTRL = 0x4
80 PCI_CAP_ID_AF = 0x13
81 PCI_AF_CAPs = 0x3
82 PCI_AF_CAPs_TP_FLR = 0x3
83 PCI_AF_CTL = 0x4
84 PCI_AF_CTL_FLR = 0x1
86 PCI_BAR_0 = 0x10
87 PCI_BAR_5 = 0x24
88 PCI_BAR_SPACE = 0x01
89 PCI_BAR_IO = 0x01
90 PCI_BAR_IO_MASK = ~0x03
91 PCI_BAR_MEM = 0x00
92 PCI_BAR_MEM_MASK = ~0x0f
93 PCI_STATUS_CAP_MASK = 0x10
94 PCI_STATUS_OFFSET = 0x6
95 PCI_CAP_OFFSET = 0x34
96 MSIX_BIR_MASK = 0x7
97 MSIX_SIZE_MASK = 0x7ff
99 # Global variable to store information from lspci
100 lspci_info = None
101 lspci_info_lock = threading.RLock()
103 #Calculate PAGE_SHIFT: number of bits to shift an address to get the page number
104 PAGE_SIZE = resource.getpagesize()
105 PAGE_SHIFT = 0
106 t = PAGE_SIZE
107 while not (t&1):
108 t>>=1
109 PAGE_SHIFT+=1
111 PAGE_MASK=~(PAGE_SIZE - 1)
112 # Definitions from Linux: include/linux/pci.h
113 def PCI_DEVFN(slot, func):
114 return ((((slot) & 0x1f) << 3) | ((func) & 0x07))
116 def parse_hex(val):
117 try:
118 if isinstance(val, types.StringTypes):
119 return int(val, 16)
120 else:
121 return val
122 except ValueError:
123 return None
125 def parse_pci_name(pci_name_string):
126 pci_match = re.match(r"((?P<domain>[0-9a-fA-F]{1,4})[:,])?" + \
127 r"(?P<bus>[0-9a-fA-F]{1,2})[:,]" + \
128 r"(?P<slot>[0-9a-fA-F]{1,2})[.,]" + \
129 r"(?P<func>[0-7])$", pci_name_string)
130 if pci_match is None:
131 raise PciDeviceParseError(('Failed to parse pci device name: %s' %
132 pci_name_string))
133 pci_dev_info = pci_match.groupdict('0')
135 domain = parse_hex(pci_dev_info['domain'])
136 bus = parse_hex(pci_dev_info['bus'])
137 slot = parse_hex(pci_dev_info['slot'])
138 func = parse_hex(pci_dev_info['func'])
140 return (domain, bus, slot, func)
142 def assigned_or_requested_vslot(dev):
143 if dev.has_key("vslot"):
144 return dev["vslot"]
145 if dev.has_key("requested_vslot"):
146 return dev["requested_vslot"]
147 raise PciDeviceVslotMissing("%s" % dev)
149 def find_sysfs_mnt():
150 try:
151 return utils.find_sysfs_mount()
152 except IOError, (errno, strerr):
153 raise PciDeviceParseError(('Failed to locate sysfs mount: %s: %s (%d)'%
154 (PROC_PCI_PATH, strerr, errno)))
155 return None
157 def get_all_pci_names():
158 sysfs_mnt = find_sysfs_mnt()
159 pci_names = os.popen('ls ' + sysfs_mnt + SYSFS_PCI_DEVS_PATH).read().split()
160 return pci_names
162 def get_all_pci_devices():
163 pci_devs = []
164 for pci_name in get_all_pci_names():
165 pci_match = re.match(r"((?P<domain>[0-9a-fA-F]{1,4})[:,])?" + \
166 r"(?P<bus>[0-9a-fA-F]{1,2})[:,]" + \
167 r"(?P<slot>[0-9a-fA-F]{1,2})[.,]" + \
168 r"(?P<func>[0-7])$", pci_name)
169 if pci_match is None:
170 raise PciDeviceParseError(('Failed to parse pci device name: %s' %
171 pci_name))
172 pci_dev_info = pci_match.groupdict('0')
173 domain = parse_hex(pci_dev_info['domain'])
174 bus = parse_hex(pci_dev_info['bus'])
175 slot = parse_hex(pci_dev_info['slot'])
176 func = parse_hex(pci_dev_info['func'])
177 try:
178 pci_dev = PciDevice(domain, bus, slot, func)
179 except:
180 continue
181 pci_devs.append(pci_dev)
183 return pci_devs
185 def _create_lspci_info():
186 """Execute 'lspci' command and parse the result.
187 If the command does not exist, lspci_info will be kept blank ({}).
189 Expects to be protected by lspci_info_lock.
190 """
191 global lspci_info
193 lspci_info = {}
195 for paragraph in os.popen(LSPCI_CMD + ' -vmm').read().split('\n\n'):
196 device_name = None
197 device_info = {}
198 for line in paragraph.split('\n'):
199 try:
200 (opt, value) = line.split(':\t')
201 if opt == 'Slot':
202 device_name = PCI_DEV_FORMAT_STR % parse_pci_name(value)
203 else:
204 device_info[opt] = value
205 except:
206 pass
207 if device_name is not None:
208 lspci_info[device_name] = device_info
210 def create_lspci_info():
211 global lspci_info_lock
212 lspci_info_lock.acquire()
213 try:
214 _create_lspci_info()
215 finally:
216 lspci_info_lock.release()
218 def save_pci_conf_space(devs_string):
219 pci_list = []
220 cfg_list = []
221 sysfs_mnt = find_sysfs_mnt()
222 for pci_str in devs_string:
223 pci_path = sysfs_mnt + SYSFS_PCI_DEVS_PATH + '/' + pci_str + \
224 SYSFS_PCI_DEV_CONFIG_PATH
225 fd = os.open(pci_path, os.O_RDONLY)
226 configs = []
227 for i in range(0, 256, 4):
228 configs = configs + [os.read(fd,4)]
229 os.close(fd)
230 pci_list = pci_list + [pci_path]
231 cfg_list = cfg_list + [configs]
232 return (pci_list, cfg_list)
234 def restore_pci_conf_space(pci_cfg_list):
235 pci_list = pci_cfg_list[0]
236 cfg_list = pci_cfg_list[1]
237 for i in range(0, len(pci_list)):
238 pci_path = pci_list[i]
239 configs = cfg_list[i]
240 fd = os.open(pci_path, os.O_WRONLY)
241 for dw in configs:
242 os.write(fd, dw)
243 os.close(fd)
245 def find_all_devices_owned_by_pciback():
246 sysfs_mnt = find_sysfs_mnt()
247 pciback_path = sysfs_mnt + SYSFS_PCIBACK_PATH
248 pci_names = os.popen('ls ' + pciback_path).read()
249 pci_list = re.findall(PCI_DEV_REG_EXPRESS_STR, pci_names)
250 dev_list = []
251 for pci in pci_list:
252 (dom, b, d, f) = parse_pci_name(pci)
253 dev = PciDevice(dom, b, d, f)
254 dev_list = dev_list + [dev]
255 return dev_list
257 def transform_list(target, src):
258 ''' src: its element is pci string (Format: xxxx:xx:xx.x).
259 target: its element is pci string, or a list of pci string.
261 If all the elements in src are in target, we remove them from target
262 and add src into target; otherwise, we remove from target all the
263 elements that also appear in src.
264 '''
265 result = []
266 target_contains_src = True
267 for e in src:
268 if not e in target:
269 target_contains_src = False
270 break
272 if target_contains_src:
273 result = result + [src]
274 for e in target:
275 if not e in src:
276 result = result + [e]
277 return result
279 def check_FLR_capability(dev_list):
280 if len(dev_list) == 0:
281 return []
283 pci_list = []
284 pci_dev_dict = {}
285 for dev in dev_list:
286 pci_list = pci_list + [dev.name]
287 pci_dev_dict[dev.name] = dev
289 while True:
290 need_transform = False
291 for pci in pci_list:
292 if isinstance(pci, types.StringTypes):
293 dev = pci_dev_dict[pci]
294 if dev.bus == 0:
295 continue
296 if dev.dev_type == DEV_TYPE_PCIe_ENDPOINT and not dev.pcie_flr:
297 coassigned_pci_list = dev.find_all_the_multi_functions()
298 need_transform = True
299 elif dev.dev_type == DEV_TYPE_PCI and not dev.pci_af_flr:
300 coassigned_pci_list = dev.find_coassigned_pci_devices(True)
301 del coassigned_pci_list[0]
302 need_transform = True
304 if need_transform:
305 pci_list = transform_list(pci_list, coassigned_pci_list)
306 if not need_transform:
307 break
309 if len(pci_list) == 0:
310 return []
312 for i in range(0, len(pci_list)):
313 if isinstance(pci_list[i], types.StringTypes):
314 pci_list[i] = [pci_list[i]]
316 # Now every element in pci_list is a list of pci string.
318 result = []
319 for pci_names in pci_list:
320 devs = []
321 for pci in pci_names:
322 devs = devs + [pci_dev_dict[pci]]
323 result = result + [devs]
324 return result
326 def check_mmio_bar(devs_list):
327 result = []
329 for dev_list in devs_list:
330 non_aligned_bar_found = False
331 for dev in dev_list:
332 if dev.has_non_page_aligned_bar:
333 non_aligned_bar_found = True
334 break
335 if not non_aligned_bar_found:
336 result = result + [dev_list]
338 return result
340 class PciDeviceNotFoundError(Exception):
341 def __init__(self,domain,bus,slot,func):
342 self.domain = domain
343 self.bus = bus
344 self.slot = slot
345 self.func = func
346 self.name = PCI_DEV_FORMAT_STR %(domain, bus, slot, func)
348 def __str__(self):
349 return ('PCI Device %s Not Found' % (self.name))
351 class PciDeviceParseError(Exception):
352 def __init__(self,msg):
353 self.message = msg
354 def __str__(self):
355 return 'Error Parsing PCI Device Info: '+self.message
357 class PciDeviceAssignmentError(Exception):
358 def __init__(self,msg):
359 self.message = msg
360 def __str__(self):
361 return 'pci: impproper device assignment spcified: ' + \
362 self.message
364 class PciDeviceVslotMissing(Exception):
365 def __init__(self,msg):
366 self.message = msg
367 def __str__(self):
368 return 'pci: no vslot or requested_vslot: ' + self.message
370 class PciDevice:
371 def __init__(self, domain, bus, slot, func):
372 self.domain = domain
373 self.bus = bus
374 self.slot = slot
375 self.func = func
376 self.name = PCI_DEV_FORMAT_STR % (domain, bus, slot, func)
377 self.cfg_space_path = find_sysfs_mnt()+SYSFS_PCI_DEVS_PATH+'/'+ \
378 self.name + SYSFS_PCI_DEV_CONFIG_PATH
379 self.irq = 0
380 self.iomem = []
381 self.ioports = []
382 self.driver = None
383 self.vendor = None
384 self.device = None
385 self.subvendor = None
386 self.subdevice = None
387 self.msix = 0
388 self.msix_iomem = []
389 self.revision = 0
390 self.classcode = None
391 self.vendorname = ""
392 self.devicename = ""
393 self.classname = ""
394 self.subvendorname = ""
395 self.subdevicename = ""
396 self.dev_type = None
397 self.has_non_page_aligned_bar = False
398 self.pcie_flr = False
399 self.pci_af_flr = False
400 self.detect_dev_info()
401 self.get_info_from_sysfs()
402 self.get_info_from_lspci()
404 def find_parent(self):
405 # i.e., /sys/bus/pci/devices/0000:00:19.0 or
406 # /sys/bus/pci/devices/0000:03:04.0
407 path = find_sysfs_mnt()+SYSFS_PCI_DEVS_PATH+'/'+ self.name
408 # i.e., ../../../devices/pci0000:00/0000:00:19.0
409 # ../../../devices/pci0000:00/0000:00:02.0/0000:01:00.2/0000:03:04.0
410 try:
411 target = os.readlink(path)
412 lst = target.split('/')
413 parent = lst[len(lst)-2]
414 if parent[0:3] == 'pci':
415 # We have reached the upmost one.
416 return None
417 else:
418 lst = parent.split(':')
419 dom = int(lst[0], 16)
420 bus = int(lst[1], 16)
421 lst = lst[2]
422 lst = lst.split('.')
423 dev = int(lst[0], 16)
424 func = int(lst[1], 16)
425 return (dom, bus, dev, func)
426 except OSError, (errno, strerr):
427 raise PciDeviceParseError('Can not locate the parent of %s',
428 self.name)
430 def find_the_uppermost_pci_bridge(self):
431 # Find the uppermost PCI/PCI-X bridge
432 dev = self.find_parent()
433 if dev is None:
434 return None
435 (dom, b, d, f) = dev
436 dev = dev_parent = PciDevice(dom, b, d, f)
437 while dev_parent.dev_type != DEV_TYPE_PCIe_BRIDGE:
438 parent = dev_parent.find_parent()
439 if parent is None:
440 break
441 (dom, b, d, f) = parent
442 dev = dev_parent
443 dev_parent = PciDevice(dom, b, d, f)
444 return dev
446 def find_all_devices_behind_the_bridge(self, ignore_bridge):
447 sysfs_mnt = find_sysfs_mnt()
448 self_path = sysfs_mnt + SYSFS_PCI_DEVS_PATH + '/' + self.name
449 pci_names = os.popen('ls ' + self_path).read()
450 dev_list = re.findall(PCI_DEV_REG_EXPRESS_STR, pci_names)
452 list = [self.name]
453 for pci_str in dev_list:
454 (dom, b, d, f) = parse_pci_name(pci_str)
455 dev = PciDevice(dom, b, d, f)
456 if dev.dev_type == DEV_TYPE_PCI_BRIDGE or \
457 dev.dev_type == DEV_TYPE_PCIe_BRIDGE:
458 sub_list_including_self = \
459 dev.find_all_devices_behind_the_bridge(ignore_bridge)
460 if ignore_bridge:
461 del sub_list_including_self[0]
462 list = list + [sub_list_including_self]
463 else:
464 list = list + [dev.name]
465 return list
467 def find_coassigned_pci_devices(self, ignore_bridge = True):
468 ''' Here'self' is a PCI device, we need find the uppermost PCI/PCI-X
469 bridge, and all devices behind it must be co-assigned to the same
470 guest.
472 Parameter:
473 [ignore_bridge]: if set, the returned result doesn't include
474 any bridge behind the uppermost PCI/PCI-X bridge.
476 Note: The first element of the return value is the uppermost
477 PCI/PCI-X bridge. If the caller doesn't need the first
478 element, the caller itself can remove it explicitly.
479 '''
480 dev = self.find_the_uppermost_pci_bridge()
482 # The 'self' device is on bus0.
483 if dev is None:
484 return [self.name]
486 dev_list = dev.find_all_devices_behind_the_bridge(ignore_bridge)
487 dev_list = re.findall(PCI_DEV_REG_EXPRESS_STR, '%s' % dev_list)
488 return dev_list
490 def do_secondary_bus_reset(self, target_bus, devs):
491 # Save the config spaces of all the devices behind the bus.
492 (pci_list, cfg_list) = save_pci_conf_space(devs)
494 #Do the Secondary Bus Reset
495 sysfs_mnt = find_sysfs_mnt()
496 parent_path = sysfs_mnt + SYSFS_PCI_DEVS_PATH + '/' + \
497 target_bus + SYSFS_PCI_DEV_CONFIG_PATH
498 fd = os.open(parent_path, os.O_RDWR)
499 os.lseek(fd, PCI_CB_BRIDGE_CONTROL, 0)
500 br_cntl = (struct.unpack('H', os.read(fd, 2)))[0]
501 # Assert Secondary Bus Reset
502 os.lseek(fd, PCI_CB_BRIDGE_CONTROL, 0)
503 br_cntl |= PCI_BRIDGE_CTL_BUS_RESET
504 os.write(fd, struct.pack('H', br_cntl))
505 time.sleep(0.100)
506 # De-assert Secondary Bus Reset
507 os.lseek(fd, PCI_CB_BRIDGE_CONTROL, 0)
508 br_cntl &= ~PCI_BRIDGE_CTL_BUS_RESET
509 os.write(fd, struct.pack('H', br_cntl))
510 time.sleep(0.100)
511 os.close(fd)
513 # Restore the config spaces
514 restore_pci_conf_space((pci_list, cfg_list))
516 def do_Dstate_transition(self):
517 pos = self.find_cap_offset(PCI_CAP_ID_PM)
518 if pos == 0:
519 return False
521 # No_Soft_Reset - When set 1, this bit indicates that
522 # devices transitioning from D3hot to D0 because of
523 # PowerState commands do not perform an internal reset.
524 pm_ctl = self.pci_conf_read32(pos + PCI_PM_CTRL)
525 if (pm_ctl & PCI_PM_CTRL_NO_SOFT_RESET) == 1:
526 return False
528 (pci_list, cfg_list) = save_pci_conf_space([self.name])
530 # Enter D3hot
531 pm_ctl &= ~PCI_PM_CTRL_STATE_MASK
532 pm_ctl |= PCI_D3hot
533 self.pci_conf_write32(pos + PCI_PM_CTRL, pm_ctl)
534 time.sleep(0.010)
536 # From D3hot to D0
537 pm_ctl &= ~PCI_PM_CTRL_STATE_MASK
538 pm_ctl |= PCI_D0hot
539 self.pci_conf_write32(pos + PCI_PM_CTRL, pm_ctl)
540 time.sleep(0.010)
542 restore_pci_conf_space((pci_list, cfg_list))
543 return True
545 def do_vendor_specific_FLR_method(self):
546 pos = self.find_cap_offset(PCI_CAP_ID_VENDOR_SPECIFIC_CAP)
547 if pos == 0:
548 return
550 vendor_id = self.pci_conf_read16(PCI_VENDOR_ID)
551 if vendor_id != VENDOR_INTEL:
552 return
554 class_id = self.pci_conf_read16(PCI_CLASS_DEVICE)
555 if class_id != PCI_CLASS_ID_USB:
556 return
558 (pci_list, cfg_list) = save_pci_conf_space([self.name])
560 self.pci_conf_write8(pos + PCI_USB_FLRCTRL, 1)
561 time.sleep(0.100)
563 restore_pci_conf_space((pci_list, cfg_list))
565 def do_FLR_for_integrated_device(self):
566 if not self.do_Dstate_transition():
567 self.do_vendor_specific_FLR_method()
569 def find_all_the_multi_functions(self):
570 sysfs_mnt = find_sysfs_mnt()
571 pci_names = os.popen('ls ' + sysfs_mnt + SYSFS_PCI_DEVS_PATH).read()
572 p = self.name
573 p = p[0 : p.rfind('.')] + '.[0-7]'
574 funcs = re.findall(p, pci_names)
575 return funcs
577 def find_coassigned_devices(self):
578 if self.dev_type == DEV_TYPE_PCIe_ENDPOINT and not self.pcie_flr:
579 return self.find_all_the_multi_functions()
580 elif self.dev_type == DEV_TYPE_PCI and not self.pci_af_flr:
581 coassigned_pci_list = self.find_coassigned_pci_devices(True)
582 if len(coassigned_pci_list) > 1:
583 del coassigned_pci_list[0]
584 return coassigned_pci_list
585 else:
586 return [self.name]
588 def find_cap_offset(self, cap):
589 path = find_sysfs_mnt()+SYSFS_PCI_DEVS_PATH+'/'+ \
590 self.name+SYSFS_PCI_DEV_CONFIG_PATH
592 pos = PCI_CAPABILITY_LIST
594 try:
595 fd = os.open(path, os.O_RDONLY)
596 os.lseek(fd, PCI_STATUS, 0)
597 status = struct.unpack('H', os.read(fd, 2))[0]
598 if (status & 0x10) == 0:
599 # The device doesn't support PCI_STATUS_CAP_LIST
600 return 0
602 max_cap = 48
603 while max_cap > 0:
604 os.lseek(fd, pos, 0)
605 pos = ord(os.read(fd, 1))
606 if pos < 0x40:
607 pos = 0
608 break;
609 os.lseek(fd, pos + 0, 0)
610 id = ord(os.read(fd, 1))
611 if id == 0xff:
612 pos = 0
613 break;
615 # Found the capability
616 if id == cap:
617 break;
619 # Test the next one
620 pos = pos + 1
621 max_cap = max_cap - 1;
623 os.close(fd)
624 except OSError, (errno, strerr):
625 raise PciDeviceParseError(('Error when accessing sysfs: %s (%d)' %
626 (strerr, errno)))
627 return pos
629 def pci_conf_read8(self, pos):
630 fd = os.open(self.cfg_space_path, os.O_RDONLY)
631 os.lseek(fd, pos, 0)
632 str = os.read(fd, 1)
633 os.close(fd)
634 val = struct.unpack('B', str)[0]
635 return val
637 def pci_conf_read16(self, pos):
638 fd = os.open(self.cfg_space_path, os.O_RDONLY)
639 os.lseek(fd, pos, 0)
640 str = os.read(fd, 2)
641 os.close(fd)
642 val = struct.unpack('H', str)[0]
643 return val
645 def pci_conf_read32(self, pos):
646 fd = os.open(self.cfg_space_path, os.O_RDONLY)
647 os.lseek(fd, pos, 0)
648 str = os.read(fd, 4)
649 os.close(fd)
650 val = struct.unpack('I', str)[0]
651 return val
653 def pci_conf_write8(self, pos, val):
654 str = struct.pack('B', val)
655 fd = os.open(self.cfg_space_path, os.O_WRONLY)
656 os.lseek(fd, pos, 0)
657 os.write(fd, str)
658 os.close(fd)
660 def pci_conf_write16(self, pos, val):
661 str = struct.pack('H', val)
662 fd = os.open(self.cfg_space_path, os.O_WRONLY)
663 os.lseek(fd, pos, 0)
664 os.write(fd, str)
665 os.close(fd)
667 def pci_conf_write32(self, pos, val):
668 str = struct.pack('I', val)
669 fd = os.open(self.cfg_space_path, os.O_WRONLY)
670 os.lseek(fd, pos, 0)
671 os.write(fd, str)
672 os.close(fd)
674 def detect_dev_info(self):
675 class_dev = self.pci_conf_read16(PCI_CLASS_DEVICE)
676 pos = self.find_cap_offset(PCI_CAP_ID_EXP)
677 if class_dev == PCI_CLASS_BRIDGE_PCI:
678 if pos == 0:
679 self.dev_type = DEV_TYPE_PCI_BRIDGE
680 else:
681 creg = self.pci_conf_read16(pos + PCI_EXP_FLAGS)
682 if ((creg & PCI_EXP_FLAGS_TYPE) >> 4) == \
683 PCI_EXP_TYPE_PCI_BRIDGE:
684 self.dev_type = DEV_TYPE_PCI_BRIDGE
685 else:
686 self.dev_type = DEV_TYPE_PCIe_BRIDGE
687 else:
688 if pos != 0:
689 self.dev_type = DEV_TYPE_PCIe_ENDPOINT
690 else:
691 self.dev_type = DEV_TYPE_PCI
693 # Force 0000:00:00.0 to be DEV_TYPE_PCIe_BRIDGE
694 if self.name == '0000:00:00.0':
695 self.dev_type = DEV_TYPE_PCIe_BRIDGE
697 if (self.dev_type == DEV_TYPE_PCI_BRIDGE) or \
698 (self.dev_type == DEV_TYPE_PCIe_BRIDGE):
699 return
701 # Try to findthe PCIe FLR capability
702 if self.dev_type == DEV_TYPE_PCIe_ENDPOINT:
703 dev_cap = self.pci_conf_read32(pos + PCI_EXP_DEVCAP)
704 if dev_cap & PCI_EXP_DEVCAP_FLR:
705 self.pcie_flr = True
706 elif self.dev_type == DEV_TYPE_PCI:
707 # Try to find the "PCI Advanced Capabilities"
708 pos = self.find_cap_offset(PCI_CAP_ID_AF)
709 if pos != 0:
710 af_cap = self.pci_conf_read8(pos + PCI_AF_CAPs)
711 if (af_cap & PCI_AF_CAPs_TP_FLR) == PCI_AF_CAPs_TP_FLR:
712 self.pci_af_flr = True
714 bar_addr = PCI_BAR_0
715 while bar_addr <= PCI_BAR_5:
716 bar = self.pci_conf_read32(bar_addr)
717 if (bar & PCI_BAR_SPACE) == PCI_BAR_MEM:
718 bar = bar & PCI_BAR_MEM_MASK
719 bar = bar & ~PAGE_MASK
720 if bar != 0:
721 self.has_non_page_aligned_bar = True
722 break
723 bar_addr = bar_addr + 4
725 def devs_check_driver(self, devs):
726 if len(devs) == 0:
727 return
728 for pci_dev in devs:
729 (dom, b, d, f) = parse_pci_name(pci_dev)
730 dev = PciDevice(dom, b, d, f)
731 if dev.driver == 'pciback':
732 continue
733 err_msg = 'pci: %s must be co-assigned to the same guest with %s' + \
734 ', but it is not owned by pciback.'
735 raise PciDeviceAssignmentError(err_msg % (pci_dev, self.name))
737 def do_FLR(self):
738 """ Perform FLR (Functional Level Reset) for the device.
739 """
740 if self.dev_type == DEV_TYPE_PCIe_ENDPOINT:
741 # If PCIe device supports FLR, we use it.
742 if self.pcie_flr:
743 (pci_list, cfg_list) = save_pci_conf_space([self.name])
744 pos = self.find_cap_offset(PCI_CAP_ID_EXP)
745 self.pci_conf_write32(pos + PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_FLR)
746 # We must sleep at least 100ms for the completion of FLR
747 time.sleep(0.100)
748 restore_pci_conf_space((pci_list, cfg_list))
749 else:
750 if self.bus == 0:
751 self.do_FLR_for_integrated_device()
752 else:
753 funcs = self.find_all_the_multi_functions()
754 self.devs_check_driver(funcs)
756 parent = '%04x:%02x:%02x.%01x' % self.find_parent()
758 # Do Secondary Bus Reset.
759 self.do_secondary_bus_reset(parent, funcs)
760 # PCI devices
761 else:
762 # For PCI device on host bus, we test "PCI Advanced Capabilities".
763 if self.bus == 0 and self.pci_af_flr:
764 (pci_list, cfg_list) = save_pci_conf_space([self.name])
765 # We use Advanced Capability to do FLR.
766 pos = self.find_cap_offset(PCI_CAP_ID_AF)
767 self.pci_conf_write8(pos + PCI_AF_CTL, PCI_AF_CTL_FLR)
768 time.sleep(0.100)
769 restore_pci_conf_space((pci_list, cfg_list))
770 else:
771 if self.bus == 0:
772 self.do_FLR_for_integrated_device()
773 else:
774 devs = self.find_coassigned_pci_devices(False)
775 # Remove the element 0 which is a bridge
776 target_bus = devs[0]
777 del devs[0]
778 self.devs_check_driver(devs)
780 # Do Secondary Bus Reset.
781 self.do_secondary_bus_reset(target_bus, devs)
783 def find_capability(self, type):
784 sysfs_mnt = find_sysfs_mnt()
785 if sysfs_mnt == None:
786 return False
787 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
788 self.name+SYSFS_PCI_DEV_CONFIG_PATH
789 try:
790 conf_file = open(path, 'rb')
791 conf_file.seek(PCI_HEADER_TYPE)
792 header_type = ord(conf_file.read(1)) & PCI_HEADER_TYPE_MASK
793 if header_type == PCI_HEADER_TYPE_CARDBUS:
794 return
795 conf_file.seek(PCI_STATUS_OFFSET)
796 status = ord(conf_file.read(1))
797 if status&PCI_STATUS_CAP_MASK:
798 conf_file.seek(PCI_CAP_OFFSET)
799 capa_pointer = ord(conf_file.read(1))
800 capa_count = 0
801 while capa_pointer:
802 if capa_pointer < 0x40:
803 raise PciDeviceParseError(
804 ('Broken capability chain: %s' % self.name))
805 capa_count += 1
806 if capa_count > 96:
807 raise PciDeviceParseError(
808 ('Looped capability chain: %s' % self.name))
809 conf_file.seek(capa_pointer)
810 capa_id = ord(conf_file.read(1))
811 capa_pointer = ord(conf_file.read(1))
812 if capa_id == type:
813 # get the type
814 message_cont_lo = ord(conf_file.read(1))
815 message_cont_hi = ord(conf_file.read(1))
816 self.msix=1
817 self.msix_entries = (message_cont_lo + \
818 (message_cont_hi << 8)) \
819 & MSIX_SIZE_MASK
820 t_off=conf_file.read(4)
821 p_off=conf_file.read(4)
822 self.table_offset=ord(t_off[0]) | (ord(t_off[1])<<8) | \
823 (ord(t_off[2])<<16)| \
824 (ord(t_off[3])<<24)
825 self.pba_offset=ord(p_off[0]) | (ord(p_off[1]) << 8)| \
826 (ord(p_off[2])<<16) | \
827 (ord(p_off[3])<<24)
828 self.table_index = self.table_offset & MSIX_BIR_MASK
829 self.table_offset = self.table_offset & ~MSIX_BIR_MASK
830 self.pba_index = self.pba_offset & MSIX_BIR_MASK
831 self.pba_offset = self.pba_offset & ~MSIX_BIR_MASK
832 break
833 except IOError, (errno, strerr):
834 raise PciDeviceParseError(('Failed to locate sysfs mount: %s: %s (%d)' %
835 (PROC_PCI_PATH, strerr, errno)))
837 def remove_msix_iomem(self, index, start, size):
838 if (index == self.table_index):
839 table_start = start+self.table_offset
840 table_end = table_start + self.msix_entries * 16
841 table_start = table_start & PAGE_MASK
842 table_end = (table_end + PAGE_SIZE) & PAGE_MASK
843 self.msix_iomem.append((table_start, table_end-table_start))
844 if (index==self.pba_index):
845 pba_start = start + self.pba_offset
846 pba_end = pba_start + self.msix_entries/8
847 pba_start = pba_start & PAGE_MASK
848 pba_end = (pba_end + PAGE_SIZE) & PAGE_MASK
849 self.msix_iomem.append((pba_start, pba_end-pba_start))
851 def get_info_from_sysfs(self):
852 self.find_capability(0x11)
853 sysfs_mnt = find_sysfs_mnt()
854 if sysfs_mnt == None:
855 return False
857 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
858 self.name+SYSFS_PCI_DEV_RESOURCE_PATH
859 try:
860 resource_file = open(path,'r')
862 for i in range(PROC_PCI_NUM_RESOURCES):
863 line = resource_file.readline()
864 sline = line.split()
865 if len(sline)<3:
866 continue
868 start = int(sline[0],16)
869 end = int(sline[1],16)
870 flags = int(sline[2],16)
871 size = end-start+1
873 if start!=0:
874 if flags&PCI_BAR_IO:
875 self.ioports.append( (start,size) )
876 else:
877 self.iomem.append( (start,size) )
878 if (self.msix):
879 self.remove_msix_iomem(i, start, size)
883 except IOError, (errno, strerr):
884 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
885 (path, strerr, errno)))
887 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
888 self.name+SYSFS_PCI_DEV_IRQ_PATH
889 try:
890 self.irq = int(open(path,'r').readline())
891 except IOError, (errno, strerr):
892 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
893 (path, strerr, errno)))
895 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
896 self.name+SYSFS_PCI_DEV_DRIVER_DIR_PATH
897 try:
898 self.driver = os.path.basename(os.readlink(path))
899 except OSError, (errno, strerr):
900 self.driver = ""
902 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
903 self.name+SYSFS_PCI_DEV_VENDOR_PATH
904 try:
905 self.vendor = int(open(path,'r').readline(), 16)
906 except IOError, (errno, strerr):
907 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
908 (path, strerr, errno)))
910 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
911 self.name+SYSFS_PCI_DEV_DEVICE_PATH
912 try:
913 self.device = int(open(path,'r').readline(), 16)
914 except IOError, (errno, strerr):
915 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
916 (path, strerr, errno)))
918 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
919 self.name+SYSFS_PCI_DEV_SUBVENDOR_PATH
920 try:
921 self.subvendor = int(open(path,'r').readline(), 16)
922 except IOError, (errno, strerr):
923 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
924 (path, strerr, errno)))
926 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
927 self.name+SYSFS_PCI_DEV_SUBDEVICE_PATH
928 try:
929 self.subdevice = int(open(path,'r').readline(), 16)
930 except IOError, (errno, strerr):
931 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
932 (path, strerr, errno)))
934 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
935 self.name+SYSFS_PCI_DEV_CLASS_PATH
936 try:
937 self.classcode = int(open(path,'r').readline(), 16)
938 except IOError, (errno, strerr):
939 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
940 (path, strerr, errno)))
942 return True
944 def get_info_from_lspci(self):
945 """ Get information such as vendor name, device name, class name, etc.
946 Since we cannot obtain these data from sysfs, use 'lspci' command.
947 """
948 global lspci_info
949 global lspci_info_lock
951 lspci_info_lock.acquire()
952 try:
953 if lspci_info is None:
954 _create_lspci_info()
956 try:
957 device_info = lspci_info[self.name]
958 self.revision = int(device_info['Rev'], 16)
959 self.vendorname = device_info['Vendor']
960 self.devicename = device_info['Device']
961 self.classname = device_info['Class']
962 self.subvendorname = device_info['SVendor']
963 self.subdevicename = device_info['SDevice']
964 except KeyError:
965 pass
967 return True
968 finally:
969 lspci_info_lock.release()
971 def __str__(self):
972 str = "PCI Device %s\n" % (self.name)
973 for (start,size) in self.ioports:
974 str = str + "IO Port 0x%02x [size=%d]\n"%(start,size)
975 for (start,size) in self.iomem:
976 str = str + "IO Mem 0x%02x [size=%d]\n"%(start,size)
977 str = str + "IRQ %d\n"%(self.irq)
978 str = str + "Vendor ID 0x%04x\n"%(self.vendor)
979 str = str + "Device ID 0x%04x\n"%(self.device)
980 str = str + "Sybsystem Vendor ID 0x%04x\n"%(self.subvendor)
981 str = str + "Subsystem Device ID 0x%04x"%(self.subdevice)
982 return str
984 def main():
985 if len(sys.argv)<5:
986 print "Usage: %s <domain> <bus> <slot> <func>\n" % sys.argv[0]
987 sys.exit(2)
989 dev = PciDevice(int(sys.argv[1],16), int(sys.argv[2],16),
990 int(sys.argv[3],16), int(sys.argv[4],16))
991 print str(dev)
993 if __name__=='__main__':
994 main()