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