ia64/xen-unstable

changeset 8880:7c720ccec00a

Tools changes for PCI front/back drivers.

Replace the old pciif DevController class with a new one that
configures the PCI backend. A util class detects the resource usage of
the specified PCI devices and pciif interacts with Xen to permit a
driver domain to directly access those physical I/O resources.

Signed-off-by: Ryan Wilson <hap9@epoch.ncsc.mil>
author kaf24@firebug.cl.cam.ac.uk
date Thu Feb 16 23:46:51 2006 +0100 (2006-02-16)
parents 5b433b4fca34
children 90ebc45e1bd8
files tools/python/xen/util/pci.py tools/python/xen/xend/server/pciif.py tools/python/xen/xm/create.py
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/tools/python/xen/util/pci.py	Thu Feb 16 23:46:51 2006 +0100
     1.3 @@ -0,0 +1,192 @@
     1.4 +#!/usr/bin/env python
     1.5 +#
     1.6 +# PCI Device Information Class
     1.7 +# - Helps obtain information about which I/O resources a PCI device needs
     1.8 +#
     1.9 +#   Author: Ryan Wilson <hap9@epoch.ncsc.mil>
    1.10 +
    1.11 +import sys
    1.12 +import os, os.path
    1.13 +
    1.14 +PROC_MNT_PATH = '/proc/mounts'
    1.15 +PROC_PCI_PATH = '/proc/bus/pci/devices'
    1.16 +PROC_PCI_NUM_RESOURCES = 7
    1.17 +
    1.18 +SYSFS_PCI_DEVS_PATH = '/bus/pci/devices'
    1.19 +SYSFS_PCI_DEV_RESOURCE_PATH = '/resource'
    1.20 +SYSFS_PCI_DEV_IRQ_PATH = '/irq'
    1.21 +SYSFS_PCI_DEV_DRIVER_DIR_PATH = '/driver'
    1.22 +
    1.23 +PCI_BAR_IO = 0x01
    1.24 +PCI_BAR_IO_MASK = ~0x03
    1.25 +PCI_BAR_MEM_MASK = ~0x0f
    1.26 +
    1.27 +# Definitions from Linux: include/linux/pci.h
    1.28 +def PCI_DEVFN(slot, func):
    1.29 +    return ((((slot) & 0x1f) << 3) | ((func) & 0x07))
    1.30 +
    1.31 +def find_sysfs_mnt():
    1.32 +    mounts_file = open(PROC_MNT_PATH,'r')
    1.33 +
    1.34 +    for line in mounts_file:
    1.35 +        sline = line.split()
    1.36 +        if len(sline)<3:
    1.37 +            continue
    1.38 +
    1.39 +        if sline[2]=='sysfs':
    1.40 +            return sline[1]
    1.41 +
    1.42 +    return None
    1.43 +
    1.44 +class PciDeviceNotFoundError(Exception):
    1.45 +    def __init__(self,domain,bus,slot,func):
    1.46 +        self.domain = domain
    1.47 +        self.bus = bus
    1.48 +        self.slot = slot
    1.49 +        self.func = func
    1.50 +        self.name = "%04x:%02x:%02x.%01x"%(domain, bus, slot, func)
    1.51 +    
    1.52 +    def __str__(self):
    1.53 +        return ('PCI Device %s Not Found' % (self.name))
    1.54 +
    1.55 +class PciDeviceParseError(Exception):
    1.56 +    def __init__(self,msg):
    1.57 +        self.message = msg
    1.58 +    def __str__(self):
    1.59 +        return 'Error Parsing PCI Device Info: '+self.message
    1.60 +
    1.61 +class PciDevice:
    1.62 +    def __init__(self, domain, bus, slot, func):
    1.63 +        self.domain = domain
    1.64 +        self.bus = bus
    1.65 +        self.slot = slot
    1.66 +        self.func = func
    1.67 +        self.name = "%04x:%02x:%02x.%01x"%(domain, bus, slot, func)
    1.68 +        self.irq = 0
    1.69 +        self.iomem = []
    1.70 +        self.ioports = []
    1.71 +        self.driver = None
    1.72 +
    1.73 +        if not self.get_info_from_sysfs():
    1.74 +            self.get_info_from_proc()
    1.75 +
    1.76 +    def get_info_from_sysfs(self):
    1.77 +        try:
    1.78 +            sysfs_mnt = find_sysfs_mnt()
    1.79 +        except IOError, (errno, strerr):
    1.80 +            raise PciDeviceParseError(('Failed to locate sysfs mount: %s (%d)' %
    1.81 +                (PROC_PCI_PATH, strerr, errno)))
    1.82 +
    1.83 +        if sysfs_mnt == None:
    1.84 +            return False
    1.85 +
    1.86 +        path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
    1.87 +                self.name+SYSFS_PCI_DEV_RESOURCE_PATH
    1.88 +        try:
    1.89 +            resource_file = open(path,'r')
    1.90 +
    1.91 +            for i in range(7):
    1.92 +                line = resource_file.readline()
    1.93 +                sline = line.split()
    1.94 +                if len(sline)<3:
    1.95 +                    continue
    1.96 +
    1.97 +                start = int(sline[0],16)
    1.98 +                end = int(sline[1],16)
    1.99 +                flags = int(sline[2],16)
   1.100 +                size = end-start+1
   1.101 +
   1.102 +                if start!=0:
   1.103 +                    if flags&PCI_BAR_IO:
   1.104 +                        self.ioports.append( (start,size) )
   1.105 +                    else:
   1.106 +                        self.iomem.append( (start,size) )
   1.107 +
   1.108 +        except IOError, (errno, strerr):
   1.109 +            raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
   1.110 +                (path, strerr, errno)))
   1.111 +
   1.112 +        path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
   1.113 +                self.name+SYSFS_PCI_DEV_IRQ_PATH
   1.114 +        try:
   1.115 +            self.irq = int(open(path,'r').readline())
   1.116 +        except IOError, (errno, strerr):
   1.117 +            raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
   1.118 +                (path, strerr, errno)))
   1.119 +
   1.120 +        path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
   1.121 +                self.name+SYSFS_PCI_DEV_DRIVER_DIR_PATH
   1.122 +        try:
   1.123 +            self.driver = os.path.basename(os.readlink(path))
   1.124 +        except IOError, (errno, strerr):
   1.125 +            raise PciDeviceParseError(('Failed to read %s: %s (%d)' %
   1.126 +                (path, strerr, errno)))
   1.127 +
   1.128 +        return True
   1.129 +        
   1.130 +    def get_info_from_proc(self):
   1.131 +        bus_devfn = '%02x%02x' % (self.bus,PCI_DEVFN(self.slot,self.func))
   1.132 +
   1.133 +        # /proc/bus/pci/devices doesn't expose domains
   1.134 +        if self.domain!=0:
   1.135 +            raise PciDeviceParseError("Can't yet detect resource usage by "+
   1.136 +                    "devices in other domains through proc!")
   1.137 +
   1.138 +        try:
   1.139 +            proc_pci_file = open(PROC_PCI_PATH,'r')
   1.140 +        except IOError, (errno, strerr):
   1.141 +            raise PciDeviceParseError(('Failed to open %s: %s (%d)' %
   1.142 +                (PROC_PCI_PATH, strerr, errno)))
   1.143 +
   1.144 +        for line in proc_pci_file:
   1.145 +            sline = line.split()
   1.146 +            if len(sline)<(PROC_PCI_NUM_RESOURCES*2+3):
   1.147 +                continue
   1.148 +
   1.149 +            if sline[0]==bus_devfn:
   1.150 +                self.dissect_proc_pci_line(sline)
   1.151 +                break
   1.152 +        else:
   1.153 +            raise PciDeviceNotFoundError(self.domain, self.bus,
   1.154 +                    self.slot, self.func)
   1.155 +
   1.156 +    def dissect_proc_pci_line(self, sline):
   1.157 +        self.irq = int(sline[2],16)
   1.158 +        start_idx = 3
   1.159 +        for i in range(PROC_PCI_NUM_RESOURCES):
   1.160 +            flags = int(sline[start_idx+i],16)
   1.161 +            size = int(sline[start_idx+i+PROC_PCI_NUM_RESOURCES],16)
   1.162 +            if flags&PCI_BAR_IO:
   1.163 +                start = flags&PCI_BAR_IO_MASK
   1.164 +                if start!=0:
   1.165 +                    self.ioports.append( (start,size) )
   1.166 +            else:
   1.167 +                start = flags&PCI_BAR_MEM_MASK
   1.168 +                if start!=0:
   1.169 +                    self.iomem.append( (start,size) )
   1.170 +
   1.171 +        # detect driver module name
   1.172 +        driver_idx = PROC_PCI_NUM_RESOURCES*2+3
   1.173 +        if len(sline)>driver_idx:
   1.174 +            self.driver = sline[driver_idx]
   1.175 +
   1.176 +    def __str__(self):
   1.177 +        str = "PCI Device %s\n" % (self.name)
   1.178 +        for (start,size) in self.ioports:
   1.179 +            str = str + "IO Port 0x%02x [size=%d]\n"%(start,size)
   1.180 +        for (start,size) in self.iomem:
   1.181 +            str = str + "IO Mem 0x%02x [size=%d]\n"%(start,size)
   1.182 +        str = str + "IRQ %d"%(self.irq)
   1.183 +        return str
   1.184 +
   1.185 +def main():
   1.186 +    if len(sys.argv)<5:
   1.187 +        print "Usage: %s <domain> <bus> <slot> <func>\n"
   1.188 +        sys.exit(2)
   1.189 +
   1.190 +    dev = PciDevice(int(sys.argv[1],16), int(sys.argv[2],16),
   1.191 +            int(sys.argv[3],16), int(sys.argv[4],16))
   1.192 +    print str(dev)
   1.193 +
   1.194 +if __name__=='__main__':
   1.195 +    main()
     2.1 --- a/tools/python/xen/xend/server/pciif.py	Thu Feb 16 23:44:41 2006 +0100
     2.2 +++ b/tools/python/xen/xend/server/pciif.py	Thu Feb 16 23:46:51 2006 +0100
     2.3 @@ -19,29 +19,28 @@
     2.4  
     2.5  import types
     2.6  
     2.7 -import xen.lowlevel.xc;
     2.8 -
     2.9  from xen.xend import sxp
    2.10  from xen.xend.XendError import VmError
    2.11 +from xen.xend.XendLogging import log
    2.12 +
    2.13 +from xen.xend.xenstore.xstransact import xstransact
    2.14  
    2.15  from xen.xend.server.DevController import DevController
    2.16  
    2.17 +import xen.lowlevel.xc
    2.18 +
    2.19 +from xen.util.pci import PciDevice
    2.20 +import resource
    2.21  
    2.22  xc = xen.lowlevel.xc.xc()
    2.23  
    2.24 -
    2.25 -def parse_pci(val):
    2.26 -    """Parse a pci field.
    2.27 -    """
    2.28 -    if isinstance(val, types.StringType):
    2.29 -        radix = 10
    2.30 -        if val.startswith('0x') or val.startswith('0X'):
    2.31 -            radix = 16
    2.32 -        v = int(val, radix)
    2.33 -    else:
    2.34 -        v = val
    2.35 -    return v
    2.36 -
    2.37 +#Calculate PAGE_SHIFT: number of bits to shift an address to get the page number
    2.38 +PAGE_SIZE = resource.getpagesize()
    2.39 +PAGE_SHIFT = 0
    2.40 +t = PAGE_SIZE
    2.41 +while not (t&1):
    2.42 +    t>>=1
    2.43 +    PAGE_SHIFT+=1
    2.44  
    2.45  class PciController(DevController):
    2.46  
    2.47 @@ -51,32 +50,110 @@ class PciController(DevController):
    2.48  
    2.49      def getDeviceDetails(self, config):
    2.50          """@see DevController.getDeviceDetails"""
    2.51 +        #log.debug('pci config='+sxp.to_string(config))
    2.52  
    2.53 -        def get_param(field):
    2.54 +        def get_param(config, field, default=None):
    2.55              try:
    2.56                  val = sxp.child_value(config, field)
    2.57  
    2.58                  if not val:
    2.59 -                    raise VmError('pci: Missing %s config setting' % field)
    2.60 +                    if default==None:
    2.61 +                        raise VmError('pci: Missing %s config setting' % field)
    2.62 +                    else:
    2.63 +                        return default
    2.64  
    2.65 -                return parse_pci(val)
    2.66 +                if isinstance(val, types.StringType):
    2.67 +                    return int(val, 16)
    2.68 +                else:
    2.69 +                    return val
    2.70              except:
    2.71 -                raise VmError('pci: Invalid config setting %s: %s' %
    2.72 +                if default==None:
    2.73 +                    raise VmError('pci: Invalid config setting %s: %s' %
    2.74                                (field, val))
    2.75 +                else:
    2.76 +                    return default
    2.77          
    2.78 -        bus  = get_param('bus')
    2.79 -        dev  = get_param('dev')
    2.80 -        func = get_param('func')
    2.81 +        back = {}
    2.82 +
    2.83 +        val = sxp.child_value(config, 'dev')
    2.84 +        if isinstance(val, list):
    2.85 +            pcidevid = 0
    2.86 +            for dev_config in sxp.children(config, 'dev'):
    2.87 +                domain = get_param(dev_config, 'domain', 0)
    2.88 +                bus = get_param(dev_config,'bus')
    2.89 +                slot = get_param(dev_config,'slot')
    2.90 +                func = get_param(dev_config,'func')
    2.91 +
    2.92 +                self.setupDevice(domain, bus, slot, func)
    2.93 +
    2.94 +                back['dev-%i'%(pcidevid)]="%04x:%02x:%02x.%02x"% \
    2.95 +                        (domain, bus, slot, func)
    2.96 +                pcidevid+=1
    2.97 +            
    2.98 +            back['num_devs']=str(pcidevid)
    2.99 +
   2.100 +        else:
   2.101 +            # Xen 2.0 configuration compatibility
   2.102 +            domain = get_param(dev_config, 'domain', 0)
   2.103 +            bus  = get_param(config, 'bus')
   2.104 +            slot = get_param(config, 'dev')
   2.105 +            func = get_param(config, 'func')
   2.106 +
   2.107 +            self.setupDevice(domain, bus, slot, func)
   2.108 +
   2.109 +            back['dev-0']="%04x:%02x:%02x.%02x"%(domain, bus, slot, func)
   2.110 +            back['num_devs']=str(1)
   2.111 +
   2.112 +        return (0, back, {})
   2.113  
   2.114 -        rc = xc.physdev_pci_access_modify(dom    = self.getDomid(),
   2.115 -                                          bus    = bus,
   2.116 -                                          dev    = dev,
   2.117 -                                          func   = func,
   2.118 -                                          enable = True)
   2.119 -        if rc < 0:
   2.120 -            #todo non-fatal
   2.121 -            raise VmError(
   2.122 -                'pci: Failed to configure device: bus=%s dev=%s func=%s' %
   2.123 -                (bus, dev, func))
   2.124 +    def setupDevice(self, domain, bus, slot, func):
   2.125 +        """ Attach I/O resources for device to frontend domain
   2.126 +        """
   2.127 +        fe_domid = self.getDomid()
   2.128 +
   2.129 +        try:
   2.130 +            dev = PciDevice(domain, bus, slot, func)
   2.131 +        except Exception, e:
   2.132 +            raise VmError("pci: failed to locate device and "+
   2.133 +                    "parse it's resources - %s"+str(e))
   2.134 +
   2.135 +        if dev.driver!='pciback':
   2.136 +            raise VmError(("pci: PCI Backend does not own device "+
   2.137 +                    "%s\n"+
   2.138 +                    "See the pciback.hide kernel "+
   2.139 +                    "command-line parameter")%(dev.name))
   2.140  
   2.141 -        return (dev, {}, {})
   2.142 +        for (start, size) in dev.ioports:
   2.143 +            log.debug('pci: enabling ioport 0x%x/0x%x'%(start,size))
   2.144 +            rc = xc.domain_ioport_permission(dom = fe_domid, first_port = start,
   2.145 +                    nr_ports = size, allow_access = True)
   2.146 +            if rc<0:
   2.147 +                raise VmError(('pci: failed to configure I/O ports on device '+
   2.148 +                            '%s - errno=%d')&(dev.name,rc))
   2.149 +            
   2.150 +        for (start, size) in dev.iomem:
   2.151 +            # Convert start/size from bytes to page frame sizes
   2.152 +            start_pfn = start>>PAGE_SHIFT
   2.153 +            # Round number of pages up to nearest page boundary (if not on one)
   2.154 +            nr_pfns = (size+(PAGE_SIZE-1))>>PAGE_SHIFT
   2.155 +
   2.156 +            log.debug('pci: enabling iomem 0x%x/0x%x pfn 0x%x/0x%x'% \
   2.157 +                    (start,size,start_pfn,nr_pfns))
   2.158 +            rc = xc.domain_iomem_permission(dom = fe_domid,
   2.159 +                    first_pfn = start_pfn,
   2.160 +                    nr_pfns = nr_pfns,
   2.161 +                    allow_access = True)
   2.162 +            if rc<0:
   2.163 +                raise VmError(('pci: failed to configure I/O memory on device '+
   2.164 +                            '%s - errno=%d')&(dev.name,rc))
   2.165 +
   2.166 +        if dev.irq>0:
   2.167 +            log.debug('pci: enabling irq %d'%dev.irq)
   2.168 +            rc = xc.domain_irq_permission(dom = fe_domid, pirq = dev.irq,
   2.169 +                    allow_access = True)
   2.170 +            if rc<0:
   2.171 +                raise VmError(('pci: failed to configure irq on device '+
   2.172 +                            '%s - errno=%d')&(dev.name,rc))
   2.173 +
   2.174 +    def waitForBackend(self,devid):
   2.175 +        return (0, "ok - no hotplug")
     3.1 --- a/tools/python/xen/xm/create.py	Thu Feb 16 23:44:41 2006 +0100
     3.2 +++ b/tools/python/xen/xm/create.py	Thu Feb 16 23:46:51 2006 +0100
     3.3 @@ -26,6 +26,7 @@ import sys
     3.4  import socket
     3.5  import commands
     3.6  import time
     3.7 +import re
     3.8  
     3.9  import xen.lowlevel.xc
    3.10  
    3.11 @@ -240,10 +241,10 @@ gopts.var('disk', val='phy:DEV,VDEV,MODE
    3.12            backend driver domain to use for the disk.
    3.13            The option may be repeated to add more than one disk.""")
    3.14  
    3.15 -gopts.var('pci', val='BUS,DEV,FUNC',
    3.16 +gopts.var('pci', val='BUS:DEV.FUNC',
    3.17            fn=append_value, default=[],
    3.18            use="""Add a PCI device to a domain, using given params (in hex).
    3.19 -         For example '-pci c0,02,1a'.
    3.20 +         For example '-pci c0:02.1a'.
    3.21           The option may be repeated to add more than one pci device.""")
    3.22  
    3.23  gopts.var('ioports', val='FROM[-TO]',
    3.24 @@ -461,8 +462,13 @@ def configure_disks(config_devs, vals):
    3.25  def configure_pci(config_devs, vals):
    3.26      """Create the config for pci devices.
    3.27      """
    3.28 -    for (bus, dev, func) in vals.pci:
    3.29 -        config_pci = ['pci', ['bus', bus], ['dev', dev], ['func', func]]
    3.30 +    config_pci = []
    3.31 +    for (domain, bus, slot, func) in vals.pci:
    3.32 +        config_pci.append(['dev', ['domain', domain], ['bus', bus], \
    3.33 +                        ['slot', slot], ['func', func]])
    3.34 +
    3.35 +    if len(config_pci)>0:
    3.36 +        config_pci.insert(0, 'pci')
    3.37          config_devs.append(['device', config_pci])
    3.38  
    3.39  def configure_ioports(config_devs, vals):
    3.40 @@ -624,13 +630,20 @@ def preprocess_disk(vals):
    3.41  def preprocess_pci(vals):
    3.42      if not vals.pci: return
    3.43      pci = []
    3.44 -    for v in vals.pci:
    3.45 -        d = v.split(',')
    3.46 -        if len(d) != 3:
    3.47 -            err('Invalid pci specifier: ' + v)
    3.48 -        # Components are in hex: add hex specifier.
    3.49 -        hexd = map(lambda v: '0x'+v, d)
    3.50 -        pci.append(hexd)
    3.51 +    for pci_dev_str in vals.pci:
    3.52 +        pci_match = re.match(r"((?P<domain>[0-9a-fA-F]{1,4})[:,])?" + \
    3.53 +                r"(?P<bus>[0-9a-fA-F]{1,2})[:,]" + \
    3.54 +                r"(?P<slot>[0-9a-fA-F]{1,2})[.,]" + \
    3.55 +                r"(?P<func>[0-9a-fA-F])", pci_dev_str)
    3.56 +        if pci_match!=None:
    3.57 +            pci_dev_info = pci_match.groupdict('0')
    3.58 +            try:
    3.59 +                pci.append( ('0x'+pci_dev_info['domain'], \
    3.60 +                        '0x'+pci_dev_info['bus'], \
    3.61 +                        '0x'+pci_dev_info['slot'], \
    3.62 +                        '0x'+pci_dev_info['func']))
    3.63 +            except IndexError:
    3.64 +                err('Error in PCI slot syntax "%s"'%(pci_dev_str))
    3.65      vals.pci = pci
    3.66  
    3.67  def preprocess_ioports(vals):