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
This commit is contained in:
		
							parent
							
								
									e1de82ea53
								
							
						
					
					
						commit
						aa67a4512e
					
				| @ -245,10 +245,17 @@ class DeviceInfo(object): | |||||||
|         self.backend_domain = backend_domain |         self.backend_domain = backend_domain | ||||||
|         #: device identifier (unique for given domain and device type) |         #: device identifier (unique for given domain and device type) | ||||||
|         self.ident = ident |         self.ident = ident | ||||||
|         #: human readable description/name of the device |         # allow redefining those as dynamic properties in subclasses | ||||||
|         self.description = description |         try: | ||||||
|         #: (running) domain to which device is currently attached |             #: human readable description/name of the device | ||||||
|         self.frontend_domain = frontend_domain |             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 |         self.data = kwargs | ||||||
| 
 | 
 | ||||||
|         if hasattr(self, 'regex'): |         if hasattr(self, 'regex'): | ||||||
|  | |||||||
							
								
								
									
										304
									
								
								qubes/ext/pci.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										304
									
								
								qubes/ext/pci.py
									
									
									
									
									
										Normal file
									
								
							| @ -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 | import qubes.tests | ||||||
| 
 | 
 | ||||||
|  | class TestVMM(object): | ||||||
|  |     def __init__(self): | ||||||
|  |         super(TestVMM, self).__init__() | ||||||
|  |         self.offline_mode = True | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class TestApp(object): | class TestApp(object): | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
|         super(TestApp, self).__init__() |         super(TestApp, self).__init__() | ||||||
|         self.domains = {} |         self.domains = {} | ||||||
|  |         self.vmm = TestVMM() | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class TestVM(qubes.vm.BaseVM): | class TestVM(qubes.vm.BaseVM): | ||||||
| @ -104,7 +111,7 @@ class TC_10_BaseVM(qubes.tests.QubesTestCase): | |||||||
| 
 | 
 | ||||||
|         self.assertItemsEqual(vm.devices.keys(), ('pci',)) |         self.assertItemsEqual(vm.devices.keys(), ('pci',)) | ||||||
|         self.assertItemsEqual(list(vm.devices['pci'].attached(persistent=True)), |         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') |         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 |     # CORE2: swallowed uses_default_kernelopts | ||||||
|     kernelopts = qubes.property('kernelopts', type=str, load_stage=4, |     kernelopts = qubes.property('kernelopts', type=str, load_stage=4, | ||||||
|         default=(lambda self: qubes.config.defaults['kernelopts_pcidevs'] |         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 self.template.kernelopts if hasattr(self, 'template') | ||||||
|             else qubes.config.defaults['kernelopts']), |             else qubes.config.defaults['kernelopts']), | ||||||
|         ls_width=30, |         ls_width=30, | ||||||
| @ -588,82 +588,6 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|                 raise qubes.exc.QubesException( |                 raise qubes.exc.QubesException( | ||||||
|                     'Failed to reset autostart for VM in systemd') |                     '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 |     # 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) |         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) |         self.libvirt_domain.createWithFlags(libvirt.VIR_DOMAIN_START_PAUSED) | ||||||
| 
 | 
 | ||||||
|         try: |         try: | ||||||
|  | |||||||
| @ -268,6 +268,7 @@ fi | |||||||
| %dir %{python_sitelib}/qubes/ext | %dir %{python_sitelib}/qubes/ext | ||||||
| %{python_sitelib}/qubes/ext/__init__.py* | %{python_sitelib}/qubes/ext/__init__.py* | ||||||
| %{python_sitelib}/qubes/ext/gui.py* | %{python_sitelib}/qubes/ext/gui.py* | ||||||
|  | %{python_sitelib}/qubes/ext/pci.py* | ||||||
| %{python_sitelib}/qubes/ext/qubesmanager.py* | %{python_sitelib}/qubes/ext/qubesmanager.py* | ||||||
| %{python_sitelib}/qubes/ext/r3compatibility.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.qubesmanager = qubes.ext.qubesmanager:QubesManager', | ||||||
|                 'qubes.ext.gui = qubes.ext.gui:GUI', |                 'qubes.ext.gui = qubes.ext.gui:GUI', | ||||||
|                 'qubes.ext.r3compatibility = qubes.ext.r3compatibility:R3Compatibility', |                 'qubes.ext.r3compatibility = qubes.ext.r3compatibility:R3Compatibility', | ||||||
|  |                 'qubes.ext.pci = qubes.ext.pci:PCIDeviceExtension', | ||||||
|             ], |             ], | ||||||
|             'qubes.devices': [ |             'qubes.devices': [ | ||||||
|                 'pci = qubes.devices:PCIDevice', |                 'pci = qubes.ext.pci:PCIDevice', | ||||||
|                 'testclass = qubes.tests.devices:TestDevice', |                 'testclass = qubes.tests.devices:TestDevice', | ||||||
|             ], |             ], | ||||||
|             'qubes.storage': [ |             'qubes.storage': [ | ||||||
|  | |||||||
| @ -27,7 +27,7 @@ | |||||||
|             <viridian/> |             <viridian/> | ||||||
|         {% endif %} |         {% endif %} | ||||||
| 
 | 
 | ||||||
|         {% if vm.devices['pci'].attached() | list |         {% if vm.devices['pci'].attached(persistent=True) | list | ||||||
|                 and vm.features.get('pci-e820-host', True) %} |                 and vm.features.get('pci-e820-host', True) %} | ||||||
|             <xen> |             <xen> | ||||||
|                 <e820_host state="on"/> |                 <e820_host state="on"/> | ||||||
| @ -94,7 +94,7 @@ | |||||||
|             {% include 'libvirt/devices/net.xml' with context %} |             {% include 'libvirt/devices/net.xml' with context %} | ||||||
|         {% endif %} |         {% endif %} | ||||||
| 
 | 
 | ||||||
|         {% for device in vm.devices.pci.attached() %} |         {% for device in vm.devices.pci.attached(persistent=True) %} | ||||||
|             {% include 'libvirt/devices/pci.xml' %} |             {% include 'libvirt/devices/pci.xml' %} | ||||||
|         {% endfor %} |         {% endfor %} | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Marek Marczykowski-Górecki
						Marek Marczykowski-Górecki