diff -r 4e1b8be54311 -r aea3485be101 tools/python/xen/xend/XendDomainInfo.py --- a/tools/python/xen/xend/XendDomainInfo.py Thu Apr 27 12:38:21 2006 +++ b/tools/python/xen/xend/XendDomainInfo.py Thu Apr 27 19:59:04 2006 @@ -1652,7 +1652,7 @@ controllerClasses[device_class] = cls -from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif +from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif, pnpif addControllerClass('vbd', blkif.BlkifController) addControllerClass('vif', netif.NetifController) addControllerClass('vtpm', tpmif.TPMifController) @@ -1660,3 +1660,4 @@ addControllerClass('ioports', iopif.IOPortsController) addControllerClass('irq', irqif.IRQController) addControllerClass('usb', usbif.UsbifController) +addControllerClass('pnp', pnpif.PnpController) diff -r 4e1b8be54311 -r aea3485be101 tools/python/xen/xm/create.py --- a/tools/python/xen/xm/create.py Thu Apr 27 12:38:21 2006 +++ b/tools/python/xen/xm/create.py Thu Apr 27 19:59:04 2006 @@ -248,6 +248,18 @@ For example 'pci=c0:02.1a'. The option may be repeated to add more than one pci device.""") +gopts.var('pnp', val='PROTOCOL:DEVICE|PNP_ID', + fn=append_value, default=[], + use="""Add a PNP device to a domain, using given params (in hex). + The device can be specified using the 'PROTOCOL:DEVICE' format + that is used within the kernel (see /sys/bus/pnp/devices) or with a + PNP ID (i.e. 'PNP0500'). Note that PNP IDs are not unique (there may + be more than one device with the same PNP ID). + For example '-pnp 00:02' or '-pnp PNP0501'. + Common PNP IDs are 'PNP0500' and 'PNP0501' for serial ports and + 'PNP0400' and 'PNP0401' for parallel ports. + The option may be repeated to add more than one pnp device.""") + gopts.var('ioports', val='FROM[-TO]', fn=append_value, default=[], use="""Add a legacy I/O range to a domain, using given params (in hex). @@ -487,6 +499,12 @@ if len(config_pci)>0: config_pci.insert(0, 'pci') config_devs.append(['device', config_pci]) + +def configure_pnp(config_devs, vals): + """Create the config for pnp devices. + """ + for name in vals.pnp: + config_devs.append(['device', ['pnp', ['name', name]]]) def configure_ioports(config_devs, vals): """Create the config for legacy i/o ranges. @@ -665,6 +683,7 @@ config_devs = [] configure_disks(config_devs, vals) configure_pci(config_devs, vals) + configure_pnp(config_devs, vals) configure_ioports(config_devs, vals) configure_irq(config_devs, vals) configure_vifs(config_devs, vals) diff -r 4e1b8be54311 -r aea3485be101 tools/python/xen/util/pnp.py --- /dev/null Thu Apr 27 12:38:21 2006 +++ b/tools/python/xen/util/pnp.py Thu Apr 27 19:59:04 2006 @@ -0,0 +1,200 @@ +#!/usr/bin/env python +# +# PNP Device Information Class +# - Helps obtain information about which I/O resources a PNP device needs +# +# Author: Ryan Wilson + +import sys +import os, os.path +import re + +PROC_MNT_PATH = '/proc/mounts' + +SYSFS_PNP_DEVS_PATH = '/bus/pnp/devices' +SYSFS_PNP_DEV_RESOURCE_PATH = '/resources' +SYSFS_PNP_DEV_DRIVER_DIR_PATH = '/driver' +SYSFS_PNP_DEV_ID_PATH = '/id' + +def find_sysfs_mnt(): + mounts_file = open(PROC_MNT_PATH,'r') + + for line in mounts_file: + sline = line.split() + if len(sline)<3: + continue + + if sline[2]=='sysfs': + return sline[1] + + return None + +class PnpDeviceNotFoundError(Exception): + def __init__(self,name): + self.name = name + + def __str__(self): + return ('PNP Device %s Not Found' % (self.name)) + +class PnpDeviceParseError(Exception): + def __init__(self,msg): + self.message = msg + def __str__(self): + return 'Error Parsing PNP Device Info: '+self.message + +class PnpDevice: + def __init__(self, name): + self.irq = [] + self.iomem = [] + self.ioports = [] + self.dma = [] + self.driver = None + self.pnp_ids = [] + + match = re.match(r"(?P[a-fA-F0-9]{1,2}):" + \ + r"(?P[a-fA-F0-9]{1,2})", name) + if match != None: + self.protocol = int(match.group('protocol'), 16) + self.device = int(match.group('device'), 16) + self.bus_id = "%02x:%02x"%(self.protocol, self.device) + else: + match = re.match(r"[a-zA-Z]{3}[a-fA-F0-9]{4}", name) + if match != None: + pnp_id = match.group() + self.bus_id = self.get_bus_id_from_pnp_id(pnp_id) + if self.bus_id == None: + raise PnpDeviceNotFound(pnp_id) + else: + raise PnpDeviceParseError("PNP Name \"%s\" is invalid"%(name)) + + self.name = self.bus_id + + if not self.get_info_from_sysfs(): + raise PnpDeviceParseError('Failed to get resource info from sysfs!') + + def get_bus_id_from_pnp_id(self, pnp_id): + """ Turn a PNP ID (an EISA ID - ex. "PNP0A03") into a bus id from sysfs + """ + try: + sysfs_mnt = find_sysfs_mnt() + except IOError, (errno, strerr): + raise PnpDeviceParseError(('Failed to locate sysfs mount: %s (%d)' % + (strerr, errno))) + + if sysfs_mnt == None: + return None + + try: + devices = os.listdir(sysfs_mnt+SYSFS_PNP_DEVS_PATH) + for bus_id in devices: + path = sysfs_mnt + SYSFS_PNP_DEVS_PATH + '/' + bus_id + \ + SYSFS_PNP_DEV_ID_PATH + id_file = open(path, 'r') + for id in id_file: + id = id.strip() + if pnp_id.upper() == id.upper(): + return bus_id + except OSError, (errno, strerr): + raise PnpDeviceParseError(('Error while locating bus_id for %s - '+\ + 'err %s (%d)')%(pnp_id, strerr, errno)) + + return None + + def get_info_from_sysfs(self): + try: + sysfs_mnt = find_sysfs_mnt() + except IOError, (errno, strerr): + raise PnpDeviceParseError(('Failed to locate sysfs mount: %s (%d)' % + (strerr, errno))) + + if sysfs_mnt == None: + return False + + path = sysfs_mnt+SYSFS_PNP_DEVS_PATH+'/'+ \ + self.bus_id+SYSFS_PNP_DEV_RESOURCE_PATH + try: + resource_file = open(path,'r') + + for line in resource_file: + line = line.strip() + sline = line.split() + + if sline[0] == 'mem': + if sline[1] != 'disabled': + mem_range = sline[1].split('-') + start = int(mem_range[0], 16) + end = int(mem_range[1], 16) + size = end-start+1 + + self.iomem.append((start,size)) + + elif sline[0] == 'io': + if sline[1] != 'disabled': + port_range = sline[1].split('-') + start = int(port_range[0], 16) + end = int(port_range[1], 16) + size = end-start+1 + + self.ioports.append((start,size)) + + elif sline[0] == 'irq': + if sline[1] != 'disabled': + self.irq.append(int(sline[1])) + + elif sline[0] == 'dma': + if sline[1] != 'disabled': + self.dma.append(int(sline[1])) + + except IOError, (errno, strerr): + raise PnpDeviceParseError(('Failed to open & read %s: %s (%d)' % \ + (path, strerr, errno))) + + path = sysfs_mnt+SYSFS_PNP_DEVS_PATH+'/'+ \ + self.bus_id+SYSFS_PNP_DEV_ID_PATH + try: + id_file = open(path,'r') + for id in id_file: + id = id.strip().upper() + self.pnp_ids.append(id) + + except IOError, (errno, strerr): + raise PnpDeviceParseError(('Failed to open & read %s: %s (%d)' % \ + (path, strerr, errno))) + + # What driver (if any) is bound to this device + path = sysfs_mnt+SYSFS_PNP_DEVS_PATH+'/'+ \ + self.bus_id+SYSFS_PNP_DEV_DRIVER_DIR_PATH + try: + self.driver = os.path.basename(os.readlink(path)) + except OSError, (errno, strerr): + # Ignore ENOENT + if errno != 2: + raise PnpDeviceParseError(('Failed to read %s: %s (%d)' % \ + (path, strerr, errno))) + + return True + + def __str__(self): + str = "PNP Device %s" % (self.name) + for pnp_id in self.pnp_ids: + str = str + "\nPNP ID %s"%(pnp_id) + for (start,size) in self.ioports: + str = str + "\nIO Port 0x%02x [size=%d]"%(start,size) + for (start,size) in self.iomem: + str = str + "\nIO Mem 0x%02x [size=%d]"%(start,size) + for irq in self.irq: + str = str + "\nIRQ %d"%(irq) + for dma in self.dma: + str = str + "\nDMA %d"%(dma) + return str + +def main(): + if len(sys.argv)<2: + print "Usage: %s \n"%(sys.argv[0]) + sys.exit(2) + + dev = PnpDevice(sys.argv[1]) + print str(dev) + +if __name__=='__main__': + main() diff -r 4e1b8be54311 -r aea3485be101 tools/python/xen/xend/server/pnpif.py --- /dev/null Thu Apr 27 12:38:21 2006 +++ b/tools/python/xen/xend/server/pnpif.py Thu Apr 27 19:59:04 2006 @@ -0,0 +1,123 @@ +#============================================================================ +# This library is free software; you can redistribute it and/or +# modify it under the terms of version 2.1 of the GNU Lesser General Public +# License as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2004, 2005 Mike Wray +# Copyright (C) 2005 XenSource Ltd +#============================================================================ + + +import types + +from xen.xend import sxp +from xen.xend.XendError import VmError +from xen.xend.XendLogging import log + +from xen.xend.xenstore.xstransact import xstransact + +from xen.xend.server.DevController import DevController + +import xen.lowlevel.xc + +from xen.util.pnp import PnpDevice +import resource + +xc = xen.lowlevel.xc.xc() + +#Calculate PAGE_SHIFT: number of bits to shift an address to get the page number +PAGE_SIZE = resource.getpagesize() +PAGE_SHIFT = 0 +t = PAGE_SIZE +while not (t&1): + t>>=1 + PAGE_SHIFT+=1 + +class PnpController(DevController): + + def __init__(self, vm): + DevController.__init__(self, vm) + + + def getDeviceDetails(self, config): + """@see DevController.getDeviceDetails""" + log.debug('pnp config='+sxp.to_string(config)) + + def get_param(config, field, default=None): + val = sxp.child_value(config, field) + + if not val: + if default==None: + raise VmError('pci: Missing %s config setting' % field) + else: + return default + + return val + + back = {} + + pnp_dev_name = sxp.child_value(config, 'name') + self.setupDevice(pnp_dev_name) + back['device'] = pnp_dev_name + + return (0, back, {}) + + def setupDevice(self, pnp_dev_name): + """ Attach I/O resources for device to frontend domain + """ + fe_domid = self.getDomid() + + try: + dev = PnpDevice(pnp_dev_name) + except Exception, e: + raise VmError("pnp: failed to locate device and "+ + "parse it's resources:\n"+str(e)) + + if dev.driver!='pnpback': + raise VmError(("pnp: PNP Backend does not own device "+ + "%s\n"+ + "See the pnpback.hide kernel "+ + "command-line parameter")%(dev.name)) + + for (start, size) in dev.ioports: + #log.debug('pnp: enabling ioport 0x%x/0x%x'%(start,size)) + rc = xc.domain_ioport_permission(dom = fe_domid, first_port = start, + nr_ports = size, allow_access = True) + if rc<0: + raise VmError(('pnp: failed to configure I/O ports on device '+ + '%s - errno=%d')%(dev.name,rc)) + + for (start, size) in dev.iomem: + # Convert start/size from bytes to page frame sizes + start_pfn = start>>PAGE_SHIFT + # Round number of pages up to nearest page boundary (if not on one) + nr_pfns = (size+(PAGE_SIZE-1))>>PAGE_SHIFT + + #log.debug('pnp: enabling iomem 0x%x/0x%x pfn 0x%x/0x%x'%(start,size,start_pfn,nr_pfns)) + rc = xc.domain_iomem_permission(dom = fe_domid, + first_pfn = start_pfn, + nr_pfns = nr_pfns, + allow_access = True) + if rc<0: + raise VmError(('pnp: failed to configure I/O memory on device '+ + '%s - errno=%d')%(dev.name,rc)) + + for irq in dev.irq: + #log.debug('pnp: enabling irq %d'%irq) + rc = xc.domain_irq_permission(dom = fe_domid, pirq = irq, + allow_access = True) + if rc<0: + raise VmError(('pnp: failed to configure irq on device '+ + '%s - errno=%d')%(dev.name,rc)) + + def waitForBackend(self,devid): + return (0, "ok - no hotplug")