ia64/xen-unstable

view tools/python/xen/util/pci.py @ 18614:365674de23c2

xend: Restore bridge control register after secondary bus reset

From: Chris Dalton <cid@hp.com>
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Fri Oct 10 11:52:26 2008 +0100 (2008-10-10)
parents a9be7b357b0b
children 98d5370fec1a
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
16 PROC_MNT_PATH = '/proc/mounts'
17 PROC_PCI_PATH = '/proc/bus/pci/devices'
18 PROC_PCI_NUM_RESOURCES = 7
20 SYSFS_PCI_DEVS_PATH = '/bus/pci/devices'
21 SYSFS_PCI_DEV_RESOURCE_PATH = '/resource'
22 SYSFS_PCI_DEV_CONFIG_PATH = '/config'
23 SYSFS_PCI_DEV_IRQ_PATH = '/irq'
24 SYSFS_PCI_DEV_DRIVER_DIR_PATH = '/driver'
25 SYSFS_PCI_DEV_VENDOR_PATH = '/vendor'
26 SYSFS_PCI_DEV_DEVICE_PATH = '/device'
27 SYSFS_PCI_DEV_SUBVENDOR_PATH = '/subsystem_vendor'
28 SYSFS_PCI_DEV_SUBDEVICE_PATH = '/subsystem_device'
29 SYSFS_PCI_DEV_CLASS_PATH = '/class'
30 SYSFS_PCIBACK_PATH = '/bus/pci/drivers/pciback/'
32 LSPCI_CMD = 'lspci'
34 PCI_DEV_REG_EXPRESS_STR = r"[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}."+ \
35 r"[0-9a-fA-F]{1}"
36 PCI_DEV_FORMAT_STR = '%04x:%02x:%02x.%01x'
38 DEV_TYPE_PCIe_ENDPOINT = 0
39 DEV_TYPE_PCIe_BRIDGE = 1
40 DEV_TYPE_PCI_BRIDGE = 2
41 DEV_TYPE_PCI = 3
43 PCI_VENDOR_ID = 0x0
44 PCI_STATUS = 0x6
45 PCI_CLASS_DEVICE = 0x0a
46 PCI_CLASS_BRIDGE_PCI = 0x0604
48 PCI_HEADER_TYPE = 0x0e
49 PCI_HEADER_TYPE_MASK = 0x7f
50 PCI_HEADER_TYPE_NORMAL = 0
51 PCI_HEADER_TYPE_BRIDGE = 1
52 PCI_HEADER_TYPE_CARDBUS = 2
54 PCI_CAPABILITY_LIST = 0x34
55 PCI_CB_BRIDGE_CONTROL = 0x3e
56 PCI_BRIDGE_CTL_BUS_RESET= 0x40
58 PCI_CAP_ID_EXP = 0x10
59 PCI_EXP_FLAGS = 0x2
60 PCI_EXP_FLAGS_TYPE = 0x00f0
61 PCI_EXP_TYPE_PCI_BRIDGE = 0x7
62 PCI_EXP_DEVCAP = 0x4
63 PCI_EXP_DEVCAP_FLR = (0x1 << 28)
64 PCI_EXP_DEVCTL = 0x8
65 PCI_EXP_DEVCTL_FLR = (0x1 << 15)
67 PCI_CAP_ID_PM = 0x01
68 PCI_PM_CTRL = 4
69 PCI_PM_CTRL_NO_SOFT_RESET = 0x0004
70 PCI_PM_CTRL_STATE_MASK = 0x0003
71 PCI_D3hot = 3
73 VENDOR_INTEL = 0x8086
74 PCI_CAP_ID_VENDOR_SPECIFIC_CAP = 0x09
75 PCI_CLASS_ID_USB = 0x0c03
76 PCI_USB_FLRCTRL = 0x4
78 PCI_CAP_ID_AF = 0x13
79 PCI_AF_CAPs = 0x3
80 PCI_AF_CAPs_TP_FLR = 0x3
81 PCI_AF_CTL = 0x4
82 PCI_AF_CTL_FLR = 0x1
84 PCI_BAR_0 = 0x10
85 PCI_BAR_5 = 0x24
86 PCI_BAR_SPACE = 0x01
87 PCI_BAR_IO = 0x01
88 PCI_BAR_IO_MASK = ~0x03
89 PCI_BAR_MEM = 0x00
90 PCI_BAR_MEM_MASK = ~0x0f
91 PCI_STATUS_CAP_MASK = 0x10
92 PCI_STATUS_OFFSET = 0x6
93 PCI_CAP_OFFSET = 0x34
94 MSIX_BIR_MASK = 0x7
95 MSIX_SIZE_MASK = 0x7ff
97 # Global variable to store information from lspci
98 lspci_info = None
100 # Global variable to store the sysfs mount point
101 sysfs_mnt_point = None
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)
143 def find_sysfs_mnt():
144 global sysfs_mnt_point
145 if not sysfs_mnt_point is None:
146 return sysfs_mnt_point
148 try:
149 mounts_file = open(PROC_MNT_PATH,'r')
151 for line in mounts_file:
152 sline = line.split()
153 if len(sline)<3:
154 continue
155 if sline[2]=='sysfs':
156 sysfs_mnt_point= sline[1]
157 return sysfs_mnt_point
158 except IOError, (errno, strerr):
159 raise PciDeviceParseError(('Failed to locate sysfs mount: %s: %s (%d)'%
160 (PROC_PCI_PATH, strerr, errno)))
161 return None
163 def get_all_pci_names():
164 sysfs_mnt = find_sysfs_mnt()
165 pci_names = os.popen('ls ' + sysfs_mnt + SYSFS_PCI_DEVS_PATH).read().split()
166 return pci_names
168 def get_all_pci_devices():
169 pci_devs = []
170 for pci_name in get_all_pci_names():
171 pci_match = re.match(r"((?P<domain>[0-9a-fA-F]{1,4})[:,])?" + \
172 r"(?P<bus>[0-9a-fA-F]{1,2})[:,]" + \
173 r"(?P<slot>[0-9a-fA-F]{1,2})[.,]" + \
174 r"(?P<func>[0-7])$", pci_name)
175 if pci_match is None:
176 raise PciDeviceParseError(('Failed to parse pci device name: %s' %
177 pci_name))
178 pci_dev_info = pci_match.groupdict('0')
179 domain = parse_hex(pci_dev_info['domain'])
180 bus = parse_hex(pci_dev_info['bus'])
181 slot = parse_hex(pci_dev_info['slot'])
182 func = parse_hex(pci_dev_info['func'])
183 try:
184 pci_dev = PciDevice(domain, bus, slot, func)
185 except:
186 continue
187 pci_devs.append(pci_dev)
189 return pci_devs
191 def create_lspci_info():
192 global lspci_info
193 lspci_info = {}
195 # Execute 'lspci' command and parse the result.
196 # If the command does not exist, lspci_info will be kept blank ({}).
197 for paragraph in os.popen(LSPCI_CMD + ' -vmm').read().split('\n\n'):
198 device_name = None
199 device_info = {}
200 for line in paragraph.split('\n'):
201 try:
202 (opt, value) = line.split(':\t')
203 if opt == 'Slot':
204 device_name = PCI_DEV_FORMAT_STR % parse_pci_name(value)
205 else:
206 device_info[opt] = value
207 except:
208 pass
209 if device_name is not None:
210 lspci_info[device_name] = device_info
212 def save_pci_conf_space(devs_string):
213 pci_list = []
214 cfg_list = []
215 sysfs_mnt = find_sysfs_mnt()
216 for pci_str in devs_string:
217 pci_path = sysfs_mnt + SYSFS_PCI_DEVS_PATH + '/' + pci_str + \
218 SYSFS_PCI_DEV_CONFIG_PATH
219 fd = os.open(pci_path, os.O_RDONLY)
220 configs = []
221 for i in range(0, 256, 4):
222 configs = configs + [os.read(fd,4)]
223 os.close(fd)
224 pci_list = pci_list + [pci_path]
225 cfg_list = cfg_list + [configs]
226 return (pci_list, cfg_list)
228 def restore_pci_conf_space(pci_cfg_list):
229 pci_list = pci_cfg_list[0]
230 cfg_list = pci_cfg_list[1]
231 for i in range(0, len(pci_list)):
232 pci_path = pci_list[i]
233 configs = cfg_list[i]
234 fd = os.open(pci_path, os.O_WRONLY)
235 for dw in configs:
236 os.write(fd, dw)
237 os.close(fd)
239 def find_all_devices_owned_by_pciback():
240 sysfs_mnt = find_sysfs_mnt()
241 pciback_path = sysfs_mnt + SYSFS_PCIBACK_PATH
242 pci_names = os.popen('ls ' + pciback_path).read()
243 pci_list = re.findall(PCI_DEV_REG_EXPRESS_STR, pci_names)
244 dev_list = []
245 for pci in pci_list:
246 (dom, b, d, f) = parse_pci_name(pci)
247 dev = PciDevice(dom, b, d, f)
248 dev_list = dev_list + [dev]
249 return dev_list
251 def transform_list(target, src):
252 ''' src: its element is pci string (Format: xxxx:xx:xx:x).
253 target: its element is pci string, or a list of pci string.
255 If all the elements in src are in target, we remove them from target
256 and add src into target; otherwise, we remove from target all the
257 elements that also appear in src.
258 '''
259 result = []
260 target_contains_src = True
261 for e in src:
262 if not e in target:
263 target_contains_src = False
264 break
266 if target_contains_src:
267 result = result + [src]
268 for e in target:
269 if not e in src:
270 result = result + [e]
271 return result
273 def check_FLR_capability(dev_list):
274 if len(dev_list) == 0:
275 return []
277 pci_list = []
278 pci_dev_dict = {}
279 for dev in dev_list:
280 pci_list = pci_list + [dev.name]
281 pci_dev_dict[dev.name] = dev
283 while True:
284 need_transform = False
285 for pci in pci_list:
286 if isinstance(pci, types.StringTypes):
287 dev = pci_dev_dict[pci]
288 if dev.bus == 0:
289 continue
290 if dev.dev_type == DEV_TYPE_PCIe_ENDPOINT and not dev.pcie_flr:
291 coassigned_pci_list = dev.find_all_the_multi_functions()
292 need_transform = True
293 elif dev.dev_type == DEV_TYPE_PCI and not dev.pci_af_flr:
294 coassigned_pci_list = dev.find_coassigned_devices(True)
295 del coassigned_pci_list[0]
296 need_transform = True
298 if need_transform:
299 pci_list = transform_list(pci_list, coassigned_pci_list)
300 if not need_transform:
301 break
303 if len(pci_list) == 0:
304 return []
306 for i in range(0, len(pci_list)):
307 if isinstance(pci_list[i], types.StringTypes):
308 pci_list[i] = [pci_list[i]]
310 # Now every element in pci_list is a list of pci string.
312 result = []
313 for pci_names in pci_list:
314 devs = []
315 for pci in pci_names:
316 devs = devs + [pci_dev_dict[pci]]
317 result = result + [devs]
318 return result
320 def check_mmio_bar(devs_list):
321 result = []
323 for dev_list in devs_list:
324 non_aligned_bar_found = False
325 for dev in dev_list:
326 if dev.has_non_page_aligned_bar:
327 non_aligned_bar_found = True
328 break
329 if not non_aligned_bar_found:
330 result = result + [dev_list]
332 return result
334 class PciDeviceNotFoundError(Exception):
335 def __init__(self,domain,bus,slot,func):
336 self.domain = domain
337 self.bus = bus
338 self.slot = slot
339 self.func = func
340 self.name = PCI_DEV_FORMAT_STR %(domain, bus, slot, func)
342 def __str__(self):
343 return ('PCI Device %s Not Found' % (self.name))
345 class PciDeviceParseError(Exception):
346 def __init__(self,msg):
347 self.message = msg
348 def __str__(self):
349 return 'Error Parsing PCI Device Info: '+self.message
351 class PciDeviceAssignmentError(Exception):
352 def __init__(self,msg):
353 self.message = msg
354 def __str__(self):
355 return 'pci: impproper device assignment spcified: ' + \
356 self.message
358 class PciDevice:
359 def __init__(self, domain, bus, slot, func):
360 self.domain = domain
361 self.bus = bus
362 self.slot = slot
363 self.func = func
364 self.name = PCI_DEV_FORMAT_STR % (domain, bus, slot, func)
365 self.cfg_space_path = find_sysfs_mnt()+SYSFS_PCI_DEVS_PATH+'/'+ \
366 self.name + SYSFS_PCI_DEV_CONFIG_PATH
367 self.irq = 0
368 self.iomem = []
369 self.ioports = []
370 self.driver = None
371 self.vendor = None
372 self.device = None
373 self.subvendor = None
374 self.subdevice = None
375 self.msix = 0
376 self.msix_iomem = []
377 self.revision = 0
378 self.classcode = None
379 self.vendorname = ""
380 self.devicename = ""
381 self.classname = ""
382 self.subvendorname = ""
383 self.subdevicename = ""
384 self.dev_type = None
385 self.has_non_page_aligned_bar = False
386 self.pcie_flr = False
387 self.pci_af_flr = False
388 self.detect_dev_info()
389 self.get_info_from_sysfs()
390 self.get_info_from_lspci()
392 def find_parent(self):
393 # i.e., /sys/bus/pci/devices/0000:00:19.0 or
394 # /sys/bus/pci/devices/0000:03:04.0
395 path = find_sysfs_mnt()+SYSFS_PCI_DEVS_PATH+'/'+ self.name
396 # i.e., ../../../devices/pci0000:00/0000:00:19.0
397 # ../../../devices/pci0000:00/0000:00:02.0/0000:01:00.2/0000:03:04.0
398 try:
399 target = os.readlink(path)
400 lst = target.split('/')
401 parent = lst[len(lst)-2]
402 if parent[0:3] == 'pci':
403 # We have reached the upmost one.
404 return None
405 else:
406 lst = parent.split(':')
407 dom = int(lst[0], 16)
408 bus = int(lst[1], 16)
409 lst = lst[2]
410 lst = lst.split('.')
411 dev = int(lst[0], 16)
412 func = int(lst[1], 16)
413 return (dom, bus, dev, func)
414 except OSError, (errno, strerr):
415 raise PciDeviceParseError('Can not locate the parent of %s',
416 self.name)
418 def find_the_uppermost_pci_bridge(self):
419 # Find the uppermost PCI/PCI-X bridge
420 (dom, b, d, f) = self.find_parent()
421 dev = dev_parent = PciDevice(dom, b, d, f)
422 while dev_parent.dev_type != DEV_TYPE_PCIe_BRIDGE:
423 parent = dev_parent.find_parent()
424 if parent is None:
425 break
426 (dom, b, d, f) = parent
427 dev = dev_parent
428 dev_parent = PciDevice(dom, b, d, f)
429 return dev
431 def find_all_devices_behind_the_bridge(self, ignore_bridge):
432 sysfs_mnt = find_sysfs_mnt()
433 self_path = sysfs_mnt + SYSFS_PCI_DEVS_PATH + '/' + self.name
434 pci_names = os.popen('ls ' + self_path).read()
435 dev_list = re.findall(PCI_DEV_REG_EXPRESS_STR, pci_names)
437 list = [self.name]
438 for pci_str in dev_list:
439 (dom, b, d, f) = parse_pci_name(pci_str)
440 dev = PciDevice(dom, b, d, f)
441 if dev.dev_type == DEV_TYPE_PCI_BRIDGE or \
442 dev.dev_type == DEV_TYPE_PCIe_BRIDGE:
443 sub_list_including_self = \
444 dev.find_all_devices_behind_the_bridge(ignore_bridge)
445 if ignore_bridge:
446 del sub_list_including_self[0]
447 list = list + [sub_list_including_self]
448 else:
449 list = list + [dev.name]
450 return list
452 def find_coassigned_devices(self, ignore_bridge = True):
453 ''' Here'self' is a PCI device, we need find the uppermost PCI/PCI-X
454 bridge, and all devices behind it must be co-assigned to the same
455 guest.
457 Parameter:
458 [ignore_bridge]: if set, the returned result doesn't include
459 any bridge behind the uppermost PCI/PCI-X bridge.
461 Note: The first element of the return value is the uppermost
462 PCI/PCI-X bridge. If the caller doesn't need the first
463 element, the caller itself can remove it explicitly.
464 '''
465 dev = self.find_the_uppermost_pci_bridge()
466 dev_list = dev.find_all_devices_behind_the_bridge(ignore_bridge)
467 dev_list = re.findall(PCI_DEV_REG_EXPRESS_STR, '%s' % dev_list)
468 return dev_list
470 def do_secondary_bus_reset(self, target_bus, devs):
471 # Save the config spaces of all the devices behind the bus.
472 (pci_list, cfg_list) = save_pci_conf_space(devs)
474 #Do the Secondary Bus Reset
475 sysfs_mnt = find_sysfs_mnt()
476 parent_path = sysfs_mnt + SYSFS_PCI_DEVS_PATH + '/' + \
477 target_bus + SYSFS_PCI_DEV_CONFIG_PATH
478 fd = os.open(parent_path, os.O_RDWR)
479 # Save state of bridge control register - restore after reset
480 os.lseek(fd, PCI_CB_BRIDGE_CONTROL, 0)
481 br_cntl = (struct.unpack('H', os.read(fd, 2)))[0]
482 # Assert Secondary Bus Reset
483 os.lseek(fd, PCI_CB_BRIDGE_CONTROL, 0)
484 os.write(fd, struct.pack('I', PCI_BRIDGE_CTL_BUS_RESET))
485 time.sleep(0.200)
486 # De-assert Secondary Bus Reset
487 os.lseek(fd, PCI_CB_BRIDGE_CONTROL, 0)
488 os.write(fd, struct.pack('H', br_cntl))
489 time.sleep(0.200)
490 os.close(fd)
492 # Restore the config spaces
493 restore_pci_conf_space((pci_list, cfg_list))
495 def do_Dstate_transition(self):
496 pos = self.find_cap_offset(PCI_CAP_ID_PM)
497 if pos == 0:
498 return False
500 (pci_list, cfg_list) = save_pci_conf_space([self.name])
502 # Enter D3hot without soft reset
503 pm_ctl = self.pci_conf_read32(pos + PCI_PM_CTRL)
504 pm_ctl |= PCI_PM_CTRL_NO_SOFT_RESET
505 pm_ctl &= ~PCI_PM_CTRL_STATE_MASK
506 pm_ctl |= PCI_D3hot
507 self.pci_conf_write32(pos + PCI_PM_CTRL, pm_ctl)
508 time.sleep(0.010)
510 # From D3hot to D0
511 self.pci_conf_write32(pos + PCI_PM_CTRL, 0)
512 time.sleep(0.010)
514 restore_pci_conf_space((pci_list, cfg_list))
515 return True
517 def do_vendor_specific_FLR_method(self):
518 pos = self.find_cap_offset(PCI_CAP_ID_VENDOR_SPECIFIC_CAP)
519 if pos == 0:
520 return
522 vendor_id = self.pci_conf_read16(PCI_VENDOR_ID)
523 if vendor_id != VENDOR_INTEL:
524 return
526 class_id = self.pci_conf_read16(PCI_CLASS_DEVICE)
527 if class_id != PCI_CLASS_ID_USB:
528 return
530 (pci_list, cfg_list) = save_pci_conf_space([self.name])
532 self.pci_conf_write8(pos + PCI_USB_FLRCTRL, 1)
533 time.sleep(0.010)
535 restore_pci_conf_space((pci_list, cfg_list))
537 def do_FLR_for_integrated_device(self):
538 if not self.do_Dstate_transition():
539 self.do_vendor_specific_FLR_method()
541 def find_all_the_multi_functions(self):
542 sysfs_mnt = find_sysfs_mnt()
543 pci_names = os.popen('ls ' + sysfs_mnt + SYSFS_PCI_DEVS_PATH).read()
544 p = self.name
545 p = p[0 : p.rfind('.')] + '.[0-7]'
546 funcs = re.findall(p, pci_names)
547 return funcs
549 def find_cap_offset(self, cap):
550 path = find_sysfs_mnt()+SYSFS_PCI_DEVS_PATH+'/'+ \
551 self.name+SYSFS_PCI_DEV_CONFIG_PATH
553 pos = PCI_CAPABILITY_LIST
555 try:
556 fd = os.open(path, os.O_RDONLY)
557 os.lseek(fd, PCI_STATUS, 0)
558 status = struct.unpack('H', os.read(fd, 2))[0]
559 if (status & 0x10) == 0:
560 # The device doesn't support PCI_STATUS_CAP_LIST
561 return 0
563 max_cap = 48
564 while max_cap > 0:
565 os.lseek(fd, pos, 0)
566 pos = ord(os.read(fd, 1))
567 if pos < 0x40:
568 pos = 0
569 break;
570 os.lseek(fd, pos + 0, 0)
571 id = ord(os.read(fd, 1))
572 if id == 0xff:
573 pos = 0
574 break;
576 # Found the capability
577 if id == cap:
578 break;
580 # Test the next one
581 pos = pos + 1
582 max_cap = max_cap - 1;
584 os.close(fd)
585 except OSError, (errno, strerr):
586 raise PciDeviceParseError(('Error when accessing sysfs: %s (%d)' %
587 (strerr, errno)))
588 return pos
590 def pci_conf_read8(self, pos):
591 fd = os.open(self.cfg_space_path, os.O_RDONLY)
592 os.lseek(fd, pos, 0)
593 str = os.read(fd, 1)
594 os.close(fd)
595 val = struct.unpack('B', str)[0]
596 return val
598 def pci_conf_read16(self, pos):
599 fd = os.open(self.cfg_space_path, os.O_RDONLY)
600 os.lseek(fd, pos, 0)
601 str = os.read(fd, 2)
602 os.close(fd)
603 val = struct.unpack('H', str)[0]
604 return val
606 def pci_conf_read32(self, pos):
607 fd = os.open(self.cfg_space_path, os.O_RDONLY)
608 os.lseek(fd, pos, 0)
609 str = os.read(fd, 4)
610 os.close(fd)
611 val = struct.unpack('I', str)[0]
612 return val
614 def pci_conf_write8(self, pos, val):
615 str = struct.pack('B', val)
616 fd = os.open(self.cfg_space_path, os.O_WRONLY)
617 os.lseek(fd, pos, 0)
618 os.write(fd, str)
619 os.close(fd)
621 def pci_conf_write16(self, pos, val):
622 str = struct.pack('H', val)
623 fd = os.open(self.cfg_space_path, os.O_WRONLY)
624 os.lseek(fd, pos, 0)
625 os.write(fd, str)
626 os.close(fd)
628 def pci_conf_write32(self, pos, val):
629 str = struct.pack('I', val)
630 fd = os.open(self.cfg_space_path, os.O_WRONLY)
631 os.lseek(fd, pos, 0)
632 os.write(fd, str)
633 os.close(fd)
635 def detect_dev_info(self):
636 class_dev = self.pci_conf_read16(PCI_CLASS_DEVICE)
637 pos = self.find_cap_offset(PCI_CAP_ID_EXP)
638 if class_dev == PCI_CLASS_BRIDGE_PCI:
639 if pos == 0:
640 self.dev_type = DEV_TYPE_PCI_BRIDGE
641 else:
642 creg = self.pci_conf_read16(pos + PCI_EXP_FLAGS)
643 if ((creg & PCI_EXP_TYPE_PCI_BRIDGE) >> 4) == \
644 PCI_EXP_TYPE_PCI_BRIDGE:
645 self.dev_type = DEV_TYPE_PCI_BRIDGE
646 else:
647 self.dev_type = DEV_TYPE_PCIe_BRIDGE
648 else:
649 if pos != 0:
650 self.dev_type = DEV_TYPE_PCIe_ENDPOINT
651 else:
652 self.dev_type = DEV_TYPE_PCI
654 # Force 0000:00:00.0 to be DEV_TYPE_PCIe_BRIDGE
655 if self.name == '0000:00:00.0':
656 self.dev_type = DEV_TYPE_PCIe_BRIDGE
658 if (self.dev_type == DEV_TYPE_PCI_BRIDGE) or \
659 (self.dev_type == DEV_TYPE_PCIe_BRIDGE):
660 return
662 # Try to findthe PCIe FLR capability
663 if self.dev_type == DEV_TYPE_PCIe_ENDPOINT:
664 dev_cap = self.pci_conf_read32(pos + PCI_EXP_DEVCAP)
665 if dev_cap & PCI_EXP_DEVCAP_FLR:
666 self.pcie_flr = True
667 elif self.dev_type == DEV_TYPE_PCI:
668 # Try to find the "PCI Advanced Capabilities"
669 pos = self.find_cap_offset(PCI_CAP_ID_AF)
670 if pos != 0:
671 af_cap = self.pci_conf_read8(pos + PCI_AF_CAPs)
672 if (af_cap & PCI_AF_CAPs_TP_FLR) == PCI_AF_CAPs_TP_FLR:
673 self.pci_af_flr = True
675 bar_addr = PCI_BAR_0
676 while bar_addr <= PCI_BAR_5:
677 bar = self.pci_conf_read32(bar_addr)
678 if (bar & PCI_BAR_SPACE) == PCI_BAR_MEM:
679 bar = bar & PCI_BAR_MEM_MASK
680 bar = bar & ~PAGE_MASK
681 if bar != 0:
682 self.has_non_page_aligned_bar = True
683 break
684 bar_addr = bar_addr + 4
686 def devs_check_driver(self, devs):
687 if len(devs) == 0:
688 return
689 for pci_dev in devs:
690 (dom, b, d, f) = parse_pci_name(pci_dev)
691 dev = PciDevice(dom, b, d, f)
692 if dev.driver == 'pciback':
693 continue
694 err_msg = 'pci: %s must be co-assigned to the same guest with %s' + \
695 ', but it is not owned by pciback.'
696 raise PciDeviceAssignmentError(err_msg % (pci_dev, self.name))
698 def do_FLR(self):
699 """ Perform FLR (Functional Level Reset) for the device.
700 """
701 if self.dev_type == DEV_TYPE_PCIe_ENDPOINT:
702 # If PCIe device supports FLR, we use it.
703 if self.pcie_flr:
704 (pci_list, cfg_list) = save_pci_conf_space([self.name])
705 pos = self.find_cap_offset(PCI_CAP_ID_EXP)
706 self.pci_conf_write32(pos + PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_FLR)
707 # We must sleep at least 100ms for the completion of FLR
708 time.sleep(0.200)
709 restore_pci_conf_space((pci_list, cfg_list))
710 else:
711 if self.bus == 0:
712 self.do_FLR_for_integrated_device()
713 else:
714 funcs = self.find_all_the_multi_functions()
715 self.devs_check_driver(funcs)
717 parent = '%04x:%02x:%02x.%01x' % self.find_parent()
719 # Do Secondary Bus Reset.
720 self.do_secondary_bus_reset(parent, funcs)
721 # PCI devices
722 else:
723 # For PCI device on host bus, we test "PCI Advanced Capabilities".
724 if self.bus == 0 and self.pci_af_flr:
725 (pci_list, cfg_list) = save_pci_conf_space([self.name])
726 # We use Advanced Capability to do FLR.
727 pos = self.find_cap_offset(PCI_CAP_ID_AF)
728 self.pci_conf_write8(pos + PCI_AF_CTL, PCI_AF_CTL_FLR)
729 time.sleep(0.200)
730 restore_pci_conf_space((pci_list, cfg_list))
731 else:
732 if self.bus == 0:
733 self.do_FLR_for_integrated_device()
734 else:
735 devs = self.find_coassigned_devices(False)
736 # Remove the element 0 which is a bridge
737 target_bus = devs[0]
738 del devs[0]
739 self.devs_check_driver(devs)
741 # Do Secondary Bus Reset.
742 self.do_secondary_bus_reset(target_bus, devs)
744 def find_capability(self, type):
745 sysfs_mnt = find_sysfs_mnt()
746 if sysfs_mnt == None:
747 return False
748 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
749 self.name+SYSFS_PCI_DEV_CONFIG_PATH
750 try:
751 conf_file = open(path, 'rb')
752 conf_file.seek(PCI_HEADER_TYPE)
753 header_type = ord(conf_file.read(1)) & PCI_HEADER_TYPE_MASK
754 if header_type == PCI_HEADER_TYPE_CARDBUS:
755 return
756 conf_file.seek(PCI_STATUS_OFFSET)
757 status = ord(conf_file.read(1))
758 if status&PCI_STATUS_CAP_MASK:
759 conf_file.seek(PCI_CAP_OFFSET)
760 capa_pointer = ord(conf_file.read(1))
761 capa_count = 0
762 while capa_pointer:
763 if capa_pointer < 0x40:
764 raise PciDeviceParseError(
765 ('Broken capability chain: %s' % self.name))
766 capa_count += 1
767 if capa_count > 96:
768 raise PciDeviceParseError(
769 ('Looped capability chain: %s' % self.name))
770 conf_file.seek(capa_pointer)
771 capa_id = ord(conf_file.read(1))
772 capa_pointer = ord(conf_file.read(1))
773 if capa_id == type:
774 # get the type
775 message_cont_lo = ord(conf_file.read(1))
776 message_cont_hi = ord(conf_file.read(1))
777 self.msix=1
778 self.msix_entries = (message_cont_lo + \
779 (message_cont_hi << 8)) \
780 & MSIX_SIZE_MASK
781 t_off=conf_file.read(4)
782 p_off=conf_file.read(4)
783 self.table_offset=ord(t_off[0]) | (ord(t_off[1])<<8) | \
784 (ord(t_off[2])<<16)| \
785 (ord(t_off[3])<<24)
786 self.pba_offset=ord(p_off[0]) | (ord(p_off[1]) << 8)| \
787 (ord(p_off[2])<<16) | \
788 (ord(p_off[3])<<24)
789 self.table_index = self.table_offset & MSIX_BIR_MASK
790 self.table_offset = self.table_offset & ~MSIX_BIR_MASK
791 self.pba_index = self.pba_offset & MSIX_BIR_MASK
792 self.pba_offset = self.pba_offset & ~MSIX_BIR_MASK
793 break
794 except IOError, (errno, strerr):
795 raise PciDeviceParseError(('Failed to locate sysfs mount: %s: %s (%d)' %
796 (PROC_PCI_PATH, strerr, errno)))
798 def remove_msix_iomem(self, index, start, size):
799 if (index == self.table_index):
800 table_start = start+self.table_offset
801 table_end = table_start + self.msix_entries * 16
802 table_start = table_start & PAGE_MASK
803 table_end = (table_end + PAGE_SIZE) & PAGE_MASK
804 self.msix_iomem.append((table_start, table_end-table_start))
805 if (index==self.pba_index):
806 pba_start = start + self.pba_offset
807 pba_end = pba_start + self.msix_entries/8
808 pba_start = pba_start & PAGE_MASK
809 pba_end = (pba_end + PAGE_SIZE) & PAGE_MASK
810 self.msix_iomem.append((pba_start, pba_end-pba_start))
812 def get_info_from_sysfs(self):
813 self.find_capability(0x11)
814 sysfs_mnt = find_sysfs_mnt()
815 if sysfs_mnt == None:
816 return False
818 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
819 self.name+SYSFS_PCI_DEV_RESOURCE_PATH
820 try:
821 resource_file = open(path,'r')
823 for i in range(PROC_PCI_NUM_RESOURCES):
824 line = resource_file.readline()
825 sline = line.split()
826 if len(sline)<3:
827 continue
829 start = int(sline[0],16)
830 end = int(sline[1],16)
831 flags = int(sline[2],16)
832 size = end-start+1
834 if start!=0:
835 if flags&PCI_BAR_IO:
836 self.ioports.append( (start,size) )
837 else:
838 self.iomem.append( (start,size) )
839 if (self.msix):
840 self.remove_msix_iomem(i, start, size)
844 except IOError, (errno, strerr):
845 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
846 (path, strerr, errno)))
848 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
849 self.name+SYSFS_PCI_DEV_IRQ_PATH
850 try:
851 self.irq = int(open(path,'r').readline())
852 except IOError, (errno, strerr):
853 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
854 (path, strerr, errno)))
856 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
857 self.name+SYSFS_PCI_DEV_DRIVER_DIR_PATH
858 try:
859 self.driver = os.path.basename(os.readlink(path))
860 except OSError, (errno, strerr):
861 self.driver = ""
863 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
864 self.name+SYSFS_PCI_DEV_VENDOR_PATH
865 try:
866 self.vendor = int(open(path,'r').readline(), 16)
867 except IOError, (errno, strerr):
868 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
869 (path, strerr, errno)))
871 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
872 self.name+SYSFS_PCI_DEV_DEVICE_PATH
873 try:
874 self.device = int(open(path,'r').readline(), 16)
875 except IOError, (errno, strerr):
876 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
877 (path, strerr, errno)))
879 path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
880 self.name+SYSFS_PCI_DEV_SUBVENDOR_PATH
881 try:
882 self.subvendor = int(open(path,'r').readline(), 16)
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_SUBDEVICE_PATH
889 try:
890 self.subdevice = int(open(path,'r').readline(), 16)
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_CLASS_PATH
897 try:
898 self.classcode = int(open(path,'r').readline(), 16)
899 except IOError, (errno, strerr):
900 raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
901 (path, strerr, errno)))
903 return True
905 def get_info_from_lspci(self):
906 """ Get information such as vendor name, device name, class name, etc.
907 Since we cannot obtain these data from sysfs, use 'lspci' command.
908 """
909 global lspci_info
911 if lspci_info is None:
912 create_lspci_info()
914 try:
915 device_info = lspci_info[self.name]
916 self.revision = int(device_info['Rev'], 16)
917 self.vendorname = device_info['Vendor']
918 self.devicename = device_info['Device']
919 self.classname = device_info['Class']
920 self.subvendorname = device_info['SVendor']
921 self.subdevicename = device_info['SDevice']
922 except KeyError:
923 pass
925 return True
927 def __str__(self):
928 str = "PCI Device %s\n" % (self.name)
929 for (start,size) in self.ioports:
930 str = str + "IO Port 0x%02x [size=%d]\n"%(start,size)
931 for (start,size) in self.iomem:
932 str = str + "IO Mem 0x%02x [size=%d]\n"%(start,size)
933 str = str + "IRQ %d\n"%(self.irq)
934 str = str + "Vendor ID 0x%04x\n"%(self.vendor)
935 str = str + "Device ID 0x%04x\n"%(self.device)
936 str = str + "Sybsystem Vendor ID 0x%04x\n"%(self.subvendor)
937 str = str + "Subsystem Device ID 0x%04x"%(self.subdevice)
938 return str
940 def main():
941 if len(sys.argv)<5:
942 print "Usage: %s <domain> <bus> <slot> <func>\n" % sys.argv[0]
943 sys.exit(2)
945 dev = PciDevice(int(sys.argv[1],16), int(sys.argv[2],16),
946 int(sys.argv[3],16), int(sys.argv[4],16))
947 print str(dev)
949 if __name__=='__main__':
950 main()