qubes/ext/pci: move PCI devices handling to an extension
Implement required event handlers according to documentation in qubes.devices. A modification of qubes.devices.DeviceInfo is needed to allow dynamic, read-only properties. QubesOS/qubes-issues#2257
Этот коммит содержится в:
		
							родитель
							
								
									e1de82ea53
								
							
						
					
					
						Коммит
						aa67a4512e
					
				@ -245,10 +245,17 @@ class DeviceInfo(object):
 | 
			
		||||
        self.backend_domain = backend_domain
 | 
			
		||||
        #: device identifier (unique for given domain and device type)
 | 
			
		||||
        self.ident = ident
 | 
			
		||||
        #: human readable description/name of the device
 | 
			
		||||
        self.description = description
 | 
			
		||||
        #: (running) domain to which device is currently attached
 | 
			
		||||
        self.frontend_domain = frontend_domain
 | 
			
		||||
        # allow redefining those as dynamic properties in subclasses
 | 
			
		||||
        try:
 | 
			
		||||
            #: human readable description/name of the device
 | 
			
		||||
            self.description = description
 | 
			
		||||
        except AttributeError:
 | 
			
		||||
            pass
 | 
			
		||||
        try:
 | 
			
		||||
            #: (running) domain to which device is currently attached
 | 
			
		||||
            self.frontend_domain = frontend_domain
 | 
			
		||||
        except AttributeError:
 | 
			
		||||
            pass
 | 
			
		||||
        self.data = kwargs
 | 
			
		||||
 | 
			
		||||
        if hasattr(self, 'regex'):
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										304
									
								
								qubes/ext/pci.py
									
									
									
									
									
										Обычный файл
									
								
							
							
						
						
									
										304
									
								
								qubes/ext/pci.py
									
									
									
									
									
										Обычный файл
									
								
							@ -0,0 +1,304 @@
 | 
			
		||||
#!/usr/bin/python2 -O
 | 
			
		||||
# vim: fileencoding=utf-8
 | 
			
		||||
#
 | 
			
		||||
# The Qubes OS Project, https://www.qubes-os.org/
 | 
			
		||||
#
 | 
			
		||||
# Copyright (C) 2016  Marek Marczykowski-Górecki
 | 
			
		||||
#                                   <marmarek@invisiblethingslab.com>
 | 
			
		||||
#
 | 
			
		||||
# This program is free software; you can redistribute it and/or modify
 | 
			
		||||
# it under the terms of the GNU General Public License as published by
 | 
			
		||||
# the Free Software Foundation; either version 2 of the License, or
 | 
			
		||||
# (at your option) any later version.
 | 
			
		||||
#
 | 
			
		||||
# This program 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 General Public License for more details.
 | 
			
		||||
#
 | 
			
		||||
# You should have received a copy of the GNU General Public License along
 | 
			
		||||
# with this program; if not, write to the Free Software Foundation, Inc.,
 | 
			
		||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 | 
			
		||||
#
 | 
			
		||||
import os
 | 
			
		||||
import re
 | 
			
		||||
import subprocess
 | 
			
		||||
import libvirt
 | 
			
		||||
import lxml
 | 
			
		||||
import lxml.etree
 | 
			
		||||
 | 
			
		||||
import qubes.devices
 | 
			
		||||
import qubes.ext
 | 
			
		||||
 | 
			
		||||
#: cache of PCI device classes
 | 
			
		||||
pci_classes = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def load_pci_classes():
 | 
			
		||||
    # 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
 | 
			
		||||
    result = {}
 | 
			
		||||
    with open('/usr/share/hwdata/pci.ids') as pciids:
 | 
			
		||||
        class_id = None
 | 
			
		||||
        subclass_id = None
 | 
			
		||||
        for line in pciids.readlines():
 | 
			
		||||
            line = line.rstrip()
 | 
			
		||||
            if line.startswith('\t\t') and class_id and subclass_id:
 | 
			
		||||
                (progif_id, _, class_name) = line[2:].split(' ', 2)
 | 
			
		||||
                result[class_id + subclass_id + progif_id] = \
 | 
			
		||||
                    class_name
 | 
			
		||||
            elif line.startswith('\t') and class_id:
 | 
			
		||||
                (subclass_id, _, class_name) = line[1:].split(' ', 2)
 | 
			
		||||
                # store both prog-if specific entry and generic one
 | 
			
		||||
                result[class_id + subclass_id + '00'] = \
 | 
			
		||||
                    class_name
 | 
			
		||||
                result[class_id + subclass_id] = \
 | 
			
		||||
                    class_name
 | 
			
		||||
            elif line.startswith('C '):
 | 
			
		||||
                (_, class_id, _, class_name) = line.split(' ', 3)
 | 
			
		||||
                result[class_id + '0000'] = class_name
 | 
			
		||||
                result[class_id + '00'] = class_name
 | 
			
		||||
                subclass_id = None
 | 
			
		||||
 | 
			
		||||
    return result
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def pcidev_class(dev_xmldesc):
 | 
			
		||||
    sysfs_path = dev_xmldesc.findtext('path')
 | 
			
		||||
    assert sysfs_path
 | 
			
		||||
    try:
 | 
			
		||||
        class_id = open(sysfs_path + '/class').read().strip()
 | 
			
		||||
    except OSError:
 | 
			
		||||
        return "Unknown"
 | 
			
		||||
 | 
			
		||||
    if not qubes.ext.pci.pci_classes:
 | 
			
		||||
        qubes.ext.pci.pci_classes = load_pci_classes()
 | 
			
		||||
    if class_id.startswith('0x'):
 | 
			
		||||
        class_id = class_id[2:]
 | 
			
		||||
    try:
 | 
			
		||||
        # ignore prog-if
 | 
			
		||||
        return qubes.ext.pci.pci_classes[class_id[0:4]]
 | 
			
		||||
    except KeyError:
 | 
			
		||||
        return "Unknown"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def attached_devices(app):
 | 
			
		||||
    """Return map device->domain-name for all currently attached devices"""
 | 
			
		||||
 | 
			
		||||
    # Libvirt do not expose nice API to query where the device is
 | 
			
		||||
    # attached. The only way would be to query _all_ the domains (
 | 
			
		||||
    # each with separate libvirt call) and look if the device is
 | 
			
		||||
    # there. Horrible waste of resources.
 | 
			
		||||
    # Instead, do this on much lower level - xenstore info for
 | 
			
		||||
    # xen-pciback driver, where we get all the info at once
 | 
			
		||||
 | 
			
		||||
    xs = app.vmm.xs
 | 
			
		||||
    devices = {}
 | 
			
		||||
    for domid in xs.ls('', 'backend/pci'):
 | 
			
		||||
        for devid in xs.ls('', 'backend/pci/' + domid):
 | 
			
		||||
            devpath = 'backend/pci/' + domid + '/' + devid
 | 
			
		||||
            domain_name = xs.read('', devpath + '/domain')
 | 
			
		||||
            try:
 | 
			
		||||
                domain = app.domains[domain_name]
 | 
			
		||||
            except KeyError:
 | 
			
		||||
                # unknown domain - maybe from another qubes.xml?
 | 
			
		||||
                continue
 | 
			
		||||
            devnum = xs.read('', devpath + '/num_devs')
 | 
			
		||||
            for dev in range(int(devnum)):
 | 
			
		||||
                dbdf = xs.read('', devpath + '/dev-' + str(dev))
 | 
			
		||||
                bdf = dbdf[len('0000:'):]
 | 
			
		||||
                devices[bdf] = domain
 | 
			
		||||
 | 
			
		||||
    return devices
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _device_desc(hostdev_xml):
 | 
			
		||||
    return '{devclass}: {vendor} {product}'.format(
 | 
			
		||||
        devclass=pcidev_class(hostdev_xml),
 | 
			
		||||
        vendor=hostdev_xml.findtext('capability/vendor'),
 | 
			
		||||
        product=hostdev_xml.findtext('capability/product'),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PCIDevice(qubes.devices.DeviceInfo):
 | 
			
		||||
    # pylint: disable=too-few-public-methods
 | 
			
		||||
    regex = re.compile(
 | 
			
		||||
        r'^(?P<bus>[0-9a-f]+):(?P<device>[0-9a-f]+)\.(?P<function>[0-9a-f]+)$')
 | 
			
		||||
    libvirt_regex = re.compile(
 | 
			
		||||
        r'^pci_0000_(?P<bus>[0-9a-f]+)_(?P<device>[0-9a-f]+)_'
 | 
			
		||||
        r'(?P<function>[0-9a-f]+)$')
 | 
			
		||||
 | 
			
		||||
    def __init__(self, backend_domain, ident, libvirt_name=None):
 | 
			
		||||
        if libvirt_name:
 | 
			
		||||
            dev_match = self.libvirt_regex.match(libvirt_name)
 | 
			
		||||
            assert dev_match
 | 
			
		||||
            ident = '{bus}:{device}.{function}'.format(**dev_match.groupdict())
 | 
			
		||||
 | 
			
		||||
        super(PCIDevice, self).__init__(backend_domain, ident, None)
 | 
			
		||||
 | 
			
		||||
        # lazy loading
 | 
			
		||||
        self._description = None
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def libvirt_name(self):
 | 
			
		||||
        # pylint: disable=no-member
 | 
			
		||||
        # noinspection PyUnresolvedReferences
 | 
			
		||||
        return 'pci_0000_{}_{}_{}'.format(self.bus, self.device, self.function)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def description(self):
 | 
			
		||||
        if self._description is None:
 | 
			
		||||
            hostdev_details = \
 | 
			
		||||
                self.backend_domain.app.vmm.libvirt_conn.nodeDeviceLookupByName(
 | 
			
		||||
                    self.libvirt_name
 | 
			
		||||
                )
 | 
			
		||||
            self._description = _device_desc(lxml.etree.fromstring(
 | 
			
		||||
                hostdev_details.XMLDesc()))
 | 
			
		||||
        return self._description
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def frontend_domain(self):
 | 
			
		||||
        # TODO: cache this
 | 
			
		||||
        all_attached = attached_devices(self.backend_domain.app)
 | 
			
		||||
        return all_attached.get(self.ident, None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PCIDeviceExtension(qubes.ext.Extension):
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        super(PCIDeviceExtension, self).__init__()
 | 
			
		||||
        # lazy load this
 | 
			
		||||
        self.pci_classes = {}
 | 
			
		||||
 | 
			
		||||
    @qubes.ext.handler('device-list:pci')
 | 
			
		||||
    def on_device_list_pci(self, vm, event):
 | 
			
		||||
        # pylint: disable=unused-argument,no-self-use
 | 
			
		||||
        # only dom0 expose PCI devices
 | 
			
		||||
        if vm.qid != 0:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        for dev in vm.app.vmm.libvirt_conn.listAllDevices():
 | 
			
		||||
            if 'pci' not in dev.listCaps():
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            xml_desc = lxml.etree.fromstring(dev.XMLDesc())
 | 
			
		||||
            libvirt_name = xml_desc.findtext('name')
 | 
			
		||||
            yield PCIDevice(vm, None, libvirt_name=libvirt_name)
 | 
			
		||||
 | 
			
		||||
    @qubes.ext.handler('device-get:pci')
 | 
			
		||||
    def on_device_get_pci(self, vm, event, ident):
 | 
			
		||||
        # pylint: disable=unused-argument,no-self-use
 | 
			
		||||
        if not vm.app.vmm.offline_mode:
 | 
			
		||||
            yield PCIDevice(vm, ident)
 | 
			
		||||
 | 
			
		||||
    @qubes.ext.handler('device-list-attached:pci')
 | 
			
		||||
    def on_device_list_attached(self, vm, event, **kwargs):
 | 
			
		||||
        # pylint: disable=unused-argument,no-self-use
 | 
			
		||||
        if not vm.is_running() or isinstance(vm, qubes.vm.adminvm.AdminVM):
 | 
			
		||||
            return
 | 
			
		||||
        xml_desc = lxml.etree.fromstring(vm.libvirt_domain.XMLDesc())
 | 
			
		||||
 | 
			
		||||
        for hostdev in xml_desc.findall('devices/hostdev'):
 | 
			
		||||
            if hostdev.get('type') != 'pci':
 | 
			
		||||
                continue
 | 
			
		||||
            address = hostdev.find('source/address')
 | 
			
		||||
            bus = address.get('bus')[2:]
 | 
			
		||||
            device = address.get('slot')[2:]
 | 
			
		||||
            function = address.get('function')[2:]
 | 
			
		||||
 | 
			
		||||
            ident = '{bus}:{device}.{function}'.format(
 | 
			
		||||
                bus=bus,
 | 
			
		||||
                device=device,
 | 
			
		||||
                function=function,
 | 
			
		||||
            )
 | 
			
		||||
            yield PCIDevice(vm.app.domains[0], ident)
 | 
			
		||||
 | 
			
		||||
    @qubes.ext.handler('device-pre-attach:pci')
 | 
			
		||||
    def on_device_pre_attached_pci(self, vm, event, device):
 | 
			
		||||
        # pylint: disable=unused-argument
 | 
			
		||||
        if not os.path.exists('/sys/bus/pci/devices/0000:{}'.format(
 | 
			
		||||
                device.ident)):
 | 
			
		||||
            raise qubes.exc.QubesException(
 | 
			
		||||
                'Invalid PCI device: {}'.format(device.ident))
 | 
			
		||||
 | 
			
		||||
        if not vm.is_running():
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            self.bind_pci_to_pciback(vm.app, device)
 | 
			
		||||
            vm.libvirt_domain.attachDevice(
 | 
			
		||||
                vm.app.env.get_template('libvirt/devices/pci.xml').render(
 | 
			
		||||
                    device=device))
 | 
			
		||||
        except subprocess.CalledProcessError as e:
 | 
			
		||||
            vm.log.exception('Failed to attach PCI device {!r} on the fly,'
 | 
			
		||||
                ' changes will be seen after VM restart.'.format(
 | 
			
		||||
                device.ident), e)
 | 
			
		||||
 | 
			
		||||
    @qubes.ext.handler('device-pre-detach:pci')
 | 
			
		||||
    def on_device_pre_detached_pci(self, vm, event, device):
 | 
			
		||||
        # pylint: disable=unused-argument,no-self-use
 | 
			
		||||
        if not vm.is_running():
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        # this cannot be converted to general API, because there is no
 | 
			
		||||
        # provision in libvirt for extracting device-side BDF; we need it for
 | 
			
		||||
        # qubes.DetachPciDevice, which unbinds driver, not to oops the kernel
 | 
			
		||||
 | 
			
		||||
        p = subprocess.Popen(['xl', 'pci-list', str(vm.xid)],
 | 
			
		||||
                stdout=subprocess.PIPE)
 | 
			
		||||
        result = p.communicate()[0]
 | 
			
		||||
        m = re.search(r'^(\d+.\d+)\s+0000:{}$'.format(device.ident), result,
 | 
			
		||||
            flags=re.MULTILINE)
 | 
			
		||||
        if not m:
 | 
			
		||||
            vm.log.error('Device %s already detached', device.ident)
 | 
			
		||||
            return
 | 
			
		||||
        vmdev = m.group(1)
 | 
			
		||||
        try:
 | 
			
		||||
            vm.run_service('qubes.DetachPciDevice',
 | 
			
		||||
                user='root', input='00:{}'.format(vmdev))
 | 
			
		||||
            vm.libvirt_domain.detachDevice(
 | 
			
		||||
                vm.app.env.get_template('libvirt/devices/pci.xml').render(
 | 
			
		||||
                    device=device))
 | 
			
		||||
        except (subprocess.CalledProcessError, libvirt.libvirtError) as e:
 | 
			
		||||
            vm.log.exception('Failed to detach PCI device {!r} on the fly,'
 | 
			
		||||
                ' changes will be seen after VM restart.'.format(
 | 
			
		||||
                device.ident), e)
 | 
			
		||||
            raise
 | 
			
		||||
 | 
			
		||||
    @qubes.ext.handler('domain-pre-start')
 | 
			
		||||
    def on_domain_pre_start(self, vm, _event, **kwargs):
 | 
			
		||||
        # Bind pci devices to pciback driver
 | 
			
		||||
        for pci in vm.devices['pci'].attached():
 | 
			
		||||
            self.bind_pci_to_pciback(vm.app, pci)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def bind_pci_to_pciback(app, device):
 | 
			
		||||
        '''Bind PCI device to pciback driver.
 | 
			
		||||
 | 
			
		||||
        :param qubes.devices.PCIDevice device: device to attach
 | 
			
		||||
 | 
			
		||||
        Devices should be unbound from their normal kernel drivers and bound to
 | 
			
		||||
        the dummy driver, which allows for attaching them to a domain.
 | 
			
		||||
        '''
 | 
			
		||||
        try:
 | 
			
		||||
            node = app.vmm.libvirt_conn.nodeDeviceLookupByName(
 | 
			
		||||
                device.libvirt_name)
 | 
			
		||||
        except libvirt.libvirtError as e:
 | 
			
		||||
            if e.get_error_code() == libvirt.VIR_ERR_NO_NODE_DEVICE:
 | 
			
		||||
                raise qubes.exc.QubesException(
 | 
			
		||||
                    'PCI device {!r} does not exist'.format(
 | 
			
		||||
                        device))
 | 
			
		||||
            raise
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            node.dettach()
 | 
			
		||||
        except libvirt.libvirtError as e:
 | 
			
		||||
            if e.get_error_code() == libvirt.VIR_ERR_INTERNAL_ERROR:
 | 
			
		||||
                # allreaddy dettached
 | 
			
		||||
                pass
 | 
			
		||||
            else:
 | 
			
		||||
                raise
 | 
			
		||||
 | 
			
		||||
@ -31,10 +31,17 @@ import qubes.vm
 | 
			
		||||
 | 
			
		||||
import qubes.tests
 | 
			
		||||
 | 
			
		||||
class TestVMM(object):
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        super(TestVMM, self).__init__()
 | 
			
		||||
        self.offline_mode = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestApp(object):
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        super(TestApp, self).__init__()
 | 
			
		||||
        self.domains = {}
 | 
			
		||||
        self.vmm = TestVMM()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestVM(qubes.vm.BaseVM):
 | 
			
		||||
@ -104,7 +111,7 @@ class TC_10_BaseVM(qubes.tests.QubesTestCase):
 | 
			
		||||
 | 
			
		||||
        self.assertItemsEqual(vm.devices.keys(), ('pci',))
 | 
			
		||||
        self.assertItemsEqual(list(vm.devices['pci'].attached(persistent=True)),
 | 
			
		||||
            [qubes.devices.PCIDevice(vm, '00:11.22')])
 | 
			
		||||
            [qubes.ext.pci.PCIDevice(vm, '00:11.22')])
 | 
			
		||||
 | 
			
		||||
        self.assertXMLIsValid(vm.__xml__(), 'domain.rng')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -243,7 +243,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
 | 
			
		||||
    # CORE2: swallowed uses_default_kernelopts
 | 
			
		||||
    kernelopts = qubes.property('kernelopts', type=str, load_stage=4,
 | 
			
		||||
        default=(lambda self: qubes.config.defaults['kernelopts_pcidevs']
 | 
			
		||||
            if list(self.devices['pci'].attached())
 | 
			
		||||
            if list(self.devices['pci'].attached(persistent=True))
 | 
			
		||||
            else self.template.kernelopts if hasattr(self, 'template')
 | 
			
		||||
            else qubes.config.defaults['kernelopts']),
 | 
			
		||||
        ls_width=30,
 | 
			
		||||
@ -588,82 +588,6 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
 | 
			
		||||
                raise qubes.exc.QubesException(
 | 
			
		||||
                    'Failed to reset autostart for VM in systemd')
 | 
			
		||||
 | 
			
		||||
    @qubes.events.handler('device-pre-attach:pci')
 | 
			
		||||
    def on_device_pre_attached_pci(self, event, device):
 | 
			
		||||
        # pylint: disable=unused-argument
 | 
			
		||||
        if not os.path.exists('/sys/bus/pci/devices/0000:{}'.format(device)):
 | 
			
		||||
            raise qubes.exc.QubesException(
 | 
			
		||||
                'Invalid PCI device: {}'.format(device))
 | 
			
		||||
 | 
			
		||||
        if not self.is_running():
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            self.bind_pci_to_pciback(device)
 | 
			
		||||
            self.libvirt_domain.attachDevice(
 | 
			
		||||
                self.app.env.get_template('libvirt/devices/pci.xml').render(
 | 
			
		||||
                    device=device))
 | 
			
		||||
        except subprocess.CalledProcessError as e:
 | 
			
		||||
            self.log.exception('Failed to attach PCI device {!r} on the fly,'
 | 
			
		||||
                ' changes will be seen after VM restart.'.format(device), e)
 | 
			
		||||
 | 
			
		||||
    @qubes.events.handler('device-pre-detach:pci')
 | 
			
		||||
    def on_device_pre_detached_pci(self, event, device):
 | 
			
		||||
        # pylint: disable=unused-argument
 | 
			
		||||
        if not self.is_running():
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        # this cannot be converted to general API, because there is no
 | 
			
		||||
        # provision in libvirt for extracting device-side BDF; we need it for
 | 
			
		||||
        # qubes.DetachPciDevice, which unbinds driver, not to oops the kernel
 | 
			
		||||
 | 
			
		||||
        p = subprocess.Popen(['xl', 'pci-list', str(self.xid)],
 | 
			
		||||
                stdout=subprocess.PIPE)
 | 
			
		||||
        result = p.communicate()[0]
 | 
			
		||||
        m = re.search(r'^(\d+.\d+)\s+0000:{}$'.format(device), result,
 | 
			
		||||
            flags=re.MULTILINE)
 | 
			
		||||
        if not m:
 | 
			
		||||
            self.log.error('Device %s already detached', device)
 | 
			
		||||
            return
 | 
			
		||||
        vmdev = m.group(1)
 | 
			
		||||
        try:
 | 
			
		||||
            self.run_service('qubes.DetachPciDevice',
 | 
			
		||||
                user='root', input='00:{}'.format(vmdev))
 | 
			
		||||
            self.libvirt_domain.detachDevice(
 | 
			
		||||
                self.app.env.get_template('libvirt/devices/pci.xml').render(
 | 
			
		||||
                    device=device))
 | 
			
		||||
        except (subprocess.CalledProcessError, libvirt.libvirtError) as e:
 | 
			
		||||
            self.log.exception('Failed to detach PCI device {!r} on the fly,'
 | 
			
		||||
                ' changes will be seen after VM restart.'.format(device), e)
 | 
			
		||||
            raise
 | 
			
		||||
 | 
			
		||||
    def bind_pci_to_pciback(self, device):
 | 
			
		||||
        '''Bind PCI device to pciback driver.
 | 
			
		||||
 | 
			
		||||
        :param qubes.devices.PCIDevice device: device to attach
 | 
			
		||||
 | 
			
		||||
        Devices should be unbound from their normal kernel drivers and bound to
 | 
			
		||||
        the dummy driver, which allows for attaching them to a domain.
 | 
			
		||||
        '''
 | 
			
		||||
        try:
 | 
			
		||||
            node = self.app.vmm.libvirt_conn.nodeDeviceLookupByName(
 | 
			
		||||
                device.libvirt_name)
 | 
			
		||||
        except libvirt.libvirtError as e:
 | 
			
		||||
            if e.get_error_code() == libvirt.VIR_ERR_NO_NODE_DEVICE:
 | 
			
		||||
                raise qubes.exc.QubesException(
 | 
			
		||||
                    'PCI device {!r} does not exist (domain {!r})'.format(
 | 
			
		||||
                        device, self.name))
 | 
			
		||||
            raise
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            node.dettach()
 | 
			
		||||
        except libvirt.libvirtError as e:
 | 
			
		||||
            if e.get_error_code() == libvirt.VIR_ERR_INTERNAL_ERROR:
 | 
			
		||||
                # allreaddy dettached
 | 
			
		||||
                pass
 | 
			
		||||
            else:
 | 
			
		||||
                raise
 | 
			
		||||
 | 
			
		||||
    #
 | 
			
		||||
    # methods for changing domain state
 | 
			
		||||
    #
 | 
			
		||||
@ -702,10 +626,6 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
 | 
			
		||||
 | 
			
		||||
        qmemman_client = self.request_memory(mem_required)
 | 
			
		||||
 | 
			
		||||
        # Bind pci devices to pciback driver
 | 
			
		||||
        for pci in self.devices['pci'].attached():
 | 
			
		||||
            self.bind_pci_to_pciback(pci)
 | 
			
		||||
 | 
			
		||||
        self.libvirt_domain.createWithFlags(libvirt.VIR_DOMAIN_START_PAUSED)
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
 | 
			
		||||
@ -268,6 +268,7 @@ fi
 | 
			
		||||
%dir %{python_sitelib}/qubes/ext
 | 
			
		||||
%{python_sitelib}/qubes/ext/__init__.py*
 | 
			
		||||
%{python_sitelib}/qubes/ext/gui.py*
 | 
			
		||||
%{python_sitelib}/qubes/ext/pci.py*
 | 
			
		||||
%{python_sitelib}/qubes/ext/qubesmanager.py*
 | 
			
		||||
%{python_sitelib}/qubes/ext/r3compatibility.py*
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										3
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								setup.py
									
									
									
									
									
								
							@ -40,9 +40,10 @@ if __name__ == '__main__':
 | 
			
		||||
                'qubes.ext.qubesmanager = qubes.ext.qubesmanager:QubesManager',
 | 
			
		||||
                'qubes.ext.gui = qubes.ext.gui:GUI',
 | 
			
		||||
                'qubes.ext.r3compatibility = qubes.ext.r3compatibility:R3Compatibility',
 | 
			
		||||
                'qubes.ext.pci = qubes.ext.pci:PCIDeviceExtension',
 | 
			
		||||
            ],
 | 
			
		||||
            'qubes.devices': [
 | 
			
		||||
                'pci = qubes.devices:PCIDevice',
 | 
			
		||||
                'pci = qubes.ext.pci:PCIDevice',
 | 
			
		||||
                'testclass = qubes.tests.devices:TestDevice',
 | 
			
		||||
            ],
 | 
			
		||||
            'qubes.storage': [
 | 
			
		||||
 | 
			
		||||
@ -27,7 +27,7 @@
 | 
			
		||||
            <viridian/>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
 | 
			
		||||
        {% if vm.devices['pci'].attached() | list
 | 
			
		||||
        {% if vm.devices['pci'].attached(persistent=True) | list
 | 
			
		||||
                and vm.features.get('pci-e820-host', True) %}
 | 
			
		||||
            <xen>
 | 
			
		||||
                <e820_host state="on"/>
 | 
			
		||||
@ -94,7 +94,7 @@
 | 
			
		||||
            {% include 'libvirt/devices/net.xml' with context %}
 | 
			
		||||
        {% endif %}
 | 
			
		||||
 | 
			
		||||
        {% for device in vm.devices.pci.attached() %}
 | 
			
		||||
        {% for device in vm.devices.pci.attached(persistent=True) %}
 | 
			
		||||
            {% include 'libvirt/devices/pci.xml' %}
 | 
			
		||||
        {% endfor %}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Загрузка…
	
		Ссылка в новой задаче
	
	Block a user