Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
437 lines (334 sloc) 13.5 KB
#!/usr/bin/python
###############################################################################
# Copyright 2008 VMware, Inc. All rights reserved. -- VMware Confidential
###############################################################################
#
# pciidlib.py
#
# Generate the following files from pci.xml:
# pci.ids
# simple.map
#
# Optional argument is the working directory where pci.xml can be found,
# and to which the output files will be written.
#
# This script will not run if any <ERROR> tags are found in pci.xml.
# Please correct the errors before running this script.
#
import sys
import getopt
import glob
import os.path
import logging
import string
import re
log = logging.Logger('pciidlib')
stderrHandler = logging.StreamHandler(sys.stderr)
stderrHandler.setLevel(logging.INFO)
stderrHandler.setFormatter(logging.Formatter('%(message)s'))
log.addHandler(stderrHandler)
import xml.dom.minidom
from xml.dom.minidom import Node
PCI_FILE = 'pci.xml'
PCIIDS_FILE = 'pci.ids'
CLASS_FILE = 'pci.classlist'
DRIVERMAP_FILE = 'simple.map'
LOCK_FILE = '/tmp/pciidlib.lck'
LSPCI_PATH = '/sbin/lspci'
HEADER = u'''#
# This file is automatically generated by pciidlib.py
# Any changes you make manually will be lost at the next build.
# Please edit <driver>.xml for permanent changes.
#
'''
VENDORLIST_HEADER = u'''# Vendors, devices and subsystems.
# Syntax:
# vendor vendor_name
# device device_name <-- single tab
# subvendor subdevice subsystem_name <-- two tabs
'''
CLASSLIST_HEADER = u'''
#
# List of known device classes, subclasses and programming interfaces
# Syntax:
# C class class_name
# subclass subclass_name <-- single tab
# prog-if prog-if_name <-- two tabs
'''
PCI_LOCATION = 0
PCI_CLASS = 1
PCI_ID = 2
PCI_CLASS_NETWORK = '0200'
PCI_CLASS_VIDEO = '0300'
class PciVendor(object):
'''Pci Id vendor object class
vendorId - pci vendor hex value
shortName - short form of the vendor name
longName - long form of the vendor name
'''
def __init__(self, vendorId, shortName='', longName=''):
self.vendorId = vendorId
self.shortName = shortName
self.longName = longName
self.devices = {}
def getDeviceList(self):
devs = self.devices.keys()
devs.sort()
return devs
class PciDevice(object):
'''Pci Id entry object class
vendorId - pci vendor id hex value
deviceId - pci device id hex value
deviceName - long form of the device name
vmwareSupported - boolean value for whether the device is supported
in vmkernel
vmwareLabel - label for vmkernel to group the type of device
usually 'scsi', 'fc', 'nic', etc.
vmwareDriverList - list of vmkernel supported drivers
pciClass - pci class associated with the device
'''
def __init__(self, vendorId, deviceId, deviceName='',
vmwareSupported=False, vmwareLabel='',
vmwareDriverList=None, pciClass=''):
self.vendorId = vendorId
self.deviceId = deviceId
self.deviceName = deviceName
self.supported = vmwareSupported
self.label = vmwareLabel
if not vmwareDriverList:
self.driverList = []
else:
self.driverList = vmwareDriverList
class PciDeviceSet(object):
def __init__(self, allowDuplicates=False, scanSystem=False):
self.allowDuplicates = allowDuplicates
self.devices = {}
self.vendors = {}
if scanSystem:
self.scanSystem()
def __getitem__(self, key):
return self.devices[key]
def __len__(self):
return len(self.devices)
def keys(self):
return self.devices.keys()
def scan(self, dirName):
log.info("Scanning %s" % dirName)
for fileName in glob.glob(os.path.join(dirName, '*.xml')):
self.parse(fileName)
def scanSystem(self, pciClass=''):
'''Scan the pci id's on the system
>>> x = PciDeviceSet()
>>> x.scanSystem()
'''
import util
pciTable = util.execWithCapture(LSPCI_PATH, [LSPCI_PATH, '-n'])
# NOTE: this does not currently populate the sub-device id info
for entry in pciTable.split('\n'):
pciEntry = entry.split(' ')
if len(pciEntry) >= 3:
devPciClass = pciEntry[PCI_CLASS].strip(':')
if pciClass and pciClass != devPciClass:
# only search for the pci class we're looking for
continue
pciText = formatPciId(pciEntry[PCI_ID])
vendorId, deviceId = pciText.split()
self.devices[pciText] = \
PciDevice(vendorId, deviceId, pciClass=devPciClass)
def parse(self, fileName):
'''Parse an xml snippet file and extract each of the pci devices.'''
def _findVmwareDrivers(tag='vmware'):
'''convenience function to parse <vmware> tags'''
supported = False
driverList = []
deviceLabel = ''
for vmwareDevice in device.getElementsByTagName(tag):
supported = True
deviceLabel = vmwareDevice.getAttribute('label')
for driver in vmwareDevice.getElementsByTagName('driver'):
driverList.append(fetchTextFromNode(driver))
return (supported, driverList, deviceLabel)
doc = xml.dom.minidom.parse(fileName)
fetchText = fetchTextFromFirstElementByTagName
# search for vendor nodes in the xml data
for vendor in doc.getElementsByTagName('vendor'):
vendorId = formatPciId(vendor.getAttribute('id'))
shortName = fetchText(vendor, 'short')
longName = fetchText(vendor, 'name')
# check to see if we already have a vendor entry
if vendorId not in self.vendors:
self.vendors[vendorId] = \
PciVendor(vendorId, shortName, longName)
else:
pciVendor = self.vendors[vendorId]
if pciVendor.longName != longName:
log.warn("Vendor name mismatch for vendor %s" % vendorId)
for device in vendor.getElementsByTagName('device'):
deviceId = formatPciId(device.getAttribute('id'))
deviceName = fetchText(device, 'name')
pciText = '%s %s' % (vendorId, deviceId)
supported, driverList, deviceLabel = _findVmwareDrivers()
# we don't allow duplicates of devices
if self.devices.has_key(pciText) and not self.allowDuplicates:
raise ValueError, "Multiple devices found for %s" % pciText
self.devices[pciText] = \
PciDevice(vendorId, deviceId, deviceName, supported,
deviceLabel, driverList)
self.vendors[vendorId].devices[pciText] = self.devices[pciText]
def parsePciids(self, fileName='/usr/share/hwdata/pci.ids'):
lines = open(fileName).readlines()
for line in lines:
line = line.rstrip()
if not line or line.strip().startswith('#'):
continue
# don't parse the pci class list
if line.startswith('C '):
break
if line[0] in string.hexdigits:
m = re.match('^([0-9a-fA-F]{4})\s+(.*)$', line)
if not m:
log.warn('Invalid pci id string "%s"' % line)
continue
vendorId, vendorName = m.groups()
vendorId = formatPciId(vendorId)
vendorName = vendorName.decode('utf-8')
self.vendors[vendorId] = \
PciVendor(vendorId, vendorName, vendorName)
continue
m = re.match('^\t([0-9a-fA-F]{4})\s+(.*)$', line)
if m:
deviceId, deviceName = m.groups()
deviceId = formatPciId(deviceId)
deviceName = deviceName.decode('utf-8')
# use the current vendor to determine the current pci id
pciText = '%s %s' % (vendorId, deviceId)
self.devices[pciText] = \
PciDevice(vendorId, deviceId, deviceName)
# populate the vendors device list
self.vendors[vendorId].devices[pciText] = self.devices[pciText]
continue
# match any entries with sub-device ids
m = re.match(
'^\t\t([0-9a-fA-F]{4})\s([0-9a-fA-F]{4})\s+(.*)$', line)
if m:
subDeviceId1, subDeviceId2, deviceName = m.groups()
subDeviceText = formatPciId(
'%s:%s' % (subDeviceId1, subDeviceId2))
# use the current vendor and device to determine the current
# pci id
deviceText = '%s %s' % (deviceId, subDeviceText)
pciText = '%s %s' % (vendorId, deviceText)
deviceName = deviceName.decode('utf-8')
self.devices[pciText] = \
PciDevice(vendorId, deviceText, deviceName)
self.vendors[vendorId].devices[pciText] = self.devices[pciText]
def formatPciId(pciidText):
'''Prepend pci id variables with '0x' since they are hex values.
>>> myPciDevice = '1000:00e0'
>>> formatPciId(myPciDevice)
'0x1000 0x00e0'
>>> myPciDevice = '1000:00E0'
>>> formatPciId(myPciDevice)
'0x1000 0x00e0'
'''
pciidText = pciidText.lower()
if pciidText.startswith('0x'):
return pciidText
return ' '.join(['0x'+y for y in pciidText.split(':')])
def fetchTextFromFirstElementByTagName(element, tag):
subElements = element.getElementsByTagName(tag)
if subElements:
return fetchTextFromNode(subElements[0])
else:
return u''
def fetchTextFromElementByTagName(element, tag):
data = u''
subElements = element.getElementsByTagName(tag)
for node in subElements:
data += fetchTextFromNode(node)
return data
def fetchTextFromNode(node):
data = u''
for subTag in node.childNodes:
if subTag.nodeType == Node.TEXT_NODE:
data += subTag.data
return data
def makeSimpleMapFile(deviceSet):
storageTypes = ['scsi', 'fc', 'ide', 'sata', 'pata']
networkTypes = ['nic']
ioatTypes = ['ioat']
buf = ''
devices = deviceSet.devices.keys()
devices.sort()
for deviceId in devices:
device = deviceSet.devices[deviceId]
# only worry about supported devices - don't bother logging since
# we only support a small subset of devices
if not device.supported:
continue
deviceIds = device.deviceId.split()
primaryId = '%s:%s' % (device.vendorId[2:],
deviceIds[0][2:])
if len(deviceIds) == 1:
subId = '0000:0000'
elif len(deviceIds) == 3:
subId = '%s:%s' % (deviceIds[1][2:],
deviceIds[2][2:])
else:
raise ValueError, "Invalid pci id for %s:%s" % \
(device.vendorId, device.deviceId)
if device.label in storageTypes:
driverType = 'storage'
elif device.label in ioatTypes:
driverType = 'ioat'
elif device.label in networkTypes:
driverType = 'network'
else:
raise ValueError, "Unknown driver type for %s" % deviceId
buf += '%s %s %s %s\n' % \
(primaryId, subId, driverType, device.driverList[0])
return buf
def makePciIdsFile(dirName, deviceSet):
buf = []
buf += HEADER.split('\n')
buf += VENDORLIST_HEADER.split('\n')
vendorTable = {}
vendors = deviceSet.vendors.keys()
vendors.sort()
for vendor in vendors:
buf.append(u"%s %s" % \
(vendor[2:], deviceSet.vendors[vendor].longName))
currentDevice = ''
deviceList = deviceSet.vendors[vendor].getDeviceList()
for pciid in deviceList:
dev = deviceSet.devices[pciid]
deviceIds = dev.deviceId.split()
# handle parent device ids
if len(deviceIds) == 1:
buf.append(u"\t%s %s" % (deviceIds[0][2:], dev.deviceName))
currentDevice = deviceIds[0]
# handle subdevice ids
elif len(deviceIds) == 3:
# it's possible to have a device with a subdevice id which
# there isn't a parent device id for, in which case we need
# to fake one
if currentDevice != deviceIds[0]:
log.warn("Missing super-entry for %s %s" % \
(vendor, deviceIds[0]))
buf.append(u"\t%s %s" % (deviceIds[0][2:], dev.deviceName))
# only write the super-entry out once
currentDevice = deviceIds[0]
buf.append(u"\t\t%s %s %s" % \
(deviceIds[1][2:], deviceIds[2][2:], dev.deviceName))
# read in the pci class file and append it to the end of the buffer
buf += CLASSLIST_HEADER.split('\n')
pciIds = '\n'.join(buf)
fileName = os.path.join(dirName, CLASS_FILE)
if os.path.exists(fileName):
f = open(fileName, 'r')
lines = f.readlines()
pciIds += ''.join(lines)
f.close()
else:
log.warn("Couldn't find pci class file.")
return pciIds