devices_pci.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. # pylint: disable=protected-access,pointless-statement
  2. #
  3. # The Qubes OS Project, https://www.qubes-os.org/
  4. #
  5. # Copyright (C) 2015-2016 Joanna Rutkowska <joanna@invisiblethingslab.com>
  6. # Copyright (C) 2015-2016 Wojtek Porczyk <woju@invisiblethingslab.com>
  7. #
  8. # This program is free software; you can redistribute it and/or modify
  9. # it under the terms of the GNU General Public License as published by
  10. # the Free Software Foundation; either version 2 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License along
  19. # with this program; if not, write to the Free Software Foundation, Inc.,
  20. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  21. #
  22. import os
  23. import subprocess
  24. import time
  25. import unittest
  26. import qubes.devices
  27. import qubes.ext.pci
  28. import qubes.tests
  29. class TC_00_Devices_PCI(qubes.tests.SystemTestsMixin,
  30. qubes.tests.QubesTestCase):
  31. def setUp(self):
  32. super(TC_00_Devices_PCI, self).setUp()
  33. if self._testMethodName not in ['test_000_list']:
  34. pcidev = os.environ.get('QUBES_TEST_PCIDEV', None)
  35. if pcidev is None:
  36. self.skipTest('Specify PCI device with QUBES_TEST_PCIDEV '
  37. 'environment variable')
  38. self.dev = self.app.domains[0].devices['pci'][pcidev]
  39. if isinstance(self.dev, qubes.devices.UnknownDevice):
  40. self.skipTest('Specified device {} does not exists'.format(pcidev))
  41. self.init_default_template()
  42. self.vm = self.app.add_new_vm(qubes.vm.appvm.AppVM,
  43. name=self.make_vm_name('vm'),
  44. label='red',
  45. )
  46. self.vm.create_on_disk()
  47. self.vm.features['pci-no-strict-reset/' + pcidev] = True
  48. self.app.save()
  49. @unittest.expectedFailure
  50. def test_000_list(self):
  51. p = subprocess.Popen(['lspci'], stdout=subprocess.PIPE)
  52. # get a dict: BDF -> description
  53. actual_devices = dict(
  54. l.split(' (')[0].split(' ', 1)
  55. for l in p.communicate()[0].decode().splitlines())
  56. for dev in self.app.domains[0].devices['pci']:
  57. self.assertIsInstance(dev, qubes.ext.pci.PCIDevice)
  58. self.assertEqual(dev.backend_domain, self.app.domains[0])
  59. self.assertIn(dev.ident, actual_devices)
  60. self.assertEqual(dev.description, actual_devices[dev.ident])
  61. self.assertIsInstance(dev.frontend_domain,
  62. (qubes.vm.BaseVM, None.__class__))
  63. actual_devices.pop(dev.ident)
  64. if actual_devices:
  65. self.fail('Not all devices listed, missing: {}'.format(
  66. actual_devices))
  67. def test_010_attach_offline(self):
  68. self.assertIsNone(self.dev.frontend_domain)
  69. self.assertNotIn(self.dev, self.vm.devices['pci'].attached())
  70. self.assertNotIn(self.dev, self.vm.devices['pci'].attached(
  71. persistent=True))
  72. self.assertNotIn(self.dev, self.vm.devices['pci'].attached(
  73. persistent=False))
  74. self.vm.devices['pci'].attach(self.dev)
  75. self.app.save()
  76. # still should be None, as domain is not started yet
  77. self.assertIsNone(self.dev.frontend_domain)
  78. self.assertIn(self.dev, self.vm.devices['pci'].attached())
  79. self.assertIn(self.dev, self.vm.devices['pci'].attached(
  80. persistent=True))
  81. self.assertNotIn(self.dev, self.vm.devices['pci'].attached(
  82. persistent=False))
  83. self.vm.start()
  84. self.assertEqual(self.dev.frontend_domain, self.vm)
  85. self.assertIn(self.dev, self.vm.devices['pci'].attached())
  86. self.assertIn(self.dev, self.vm.devices['pci'].attached(
  87. persistent=True))
  88. self.assertNotIn(self.dev, self.vm.devices['pci'].attached(
  89. persistent=False))
  90. p = self.vm.run('lspci', passio_popen=True)
  91. (stdout, _) = p.communicate()
  92. self.assertIn(self.dev.description, stdout)
  93. def test_011_attach_online(self):
  94. self.vm.start()
  95. self.vm.devices['pci'].attach(self.dev)
  96. self.assertEqual(self.dev.frontend_domain, self.vm)
  97. self.assertIn(self.dev, self.vm.devices['pci'].attached())
  98. self.assertIn(self.dev, self.vm.devices['pci'].attached(
  99. persistent=True))
  100. self.assertNotIn(self.dev, self.vm.devices['pci'].attached(
  101. persistent=False))
  102. # give VM kernel some time to discover new device
  103. time.sleep(1)
  104. p = self.vm.run('lspci', passio_popen=True)
  105. (stdout, _) = p.communicate()
  106. self.assertIn(self.dev.description, stdout)
  107. def test_012_attach_online_temp(self):
  108. self.vm.start()
  109. self.vm.devices['pci'].attach(self.dev, persistent=False)
  110. self.assertEqual(self.dev.frontend_domain, self.vm)
  111. self.assertIn(self.dev, self.vm.devices['pci'].attached())
  112. self.assertNotIn(self.dev, self.vm.devices['pci'].attached(
  113. persistent=True))
  114. self.assertIn(self.dev, self.vm.devices['pci'].attached(
  115. persistent=False))
  116. # give VM kernel some time to discover new device
  117. time.sleep(1)
  118. p = self.vm.run('lspci', passio_popen=True)
  119. (stdout, _) = p.communicate()
  120. self.assertIn(self.dev.description, stdout)
  121. def test_020_detach_online(self):
  122. self.vm.devices['pci'].attach(self.dev)
  123. self.app.save()
  124. self.vm.start()
  125. self.assertIn(self.dev, self.vm.devices['pci'].attached())
  126. self.assertIn(self.dev, self.vm.devices['pci'].attached(
  127. persistent=True))
  128. self.assertNotIn(self.dev, self.vm.devices['pci'].attached(
  129. persistent=False))
  130. self.assertEqual(self.dev.frontend_domain, self.vm)
  131. self.vm.devices['pci'].detach(self.dev)
  132. self.assertIsNone(self.dev.frontend_domain)
  133. self.assertNotIn(self.dev, self.vm.devices['pci'].attached())
  134. self.assertNotIn(self.dev, self.vm.devices['pci'].attached(
  135. persistent=True))
  136. self.assertNotIn(self.dev, self.vm.devices['pci'].attached(
  137. persistent=False))
  138. p = self.vm.run('lspci', passio_popen=True)
  139. (stdout, _) = p.communicate()
  140. self.assertNotIn(self.dev.description, stdout)
  141. # can't do this right now because of kernel bug - it cause the whole
  142. # PCI bus being deregistered, which emit some warning in sysfs
  143. # handling code (removing non-existing "0000:00" group)
  144. #
  145. # p = self.vm.run('dmesg', passio_popen=True)
  146. # (stdout, _) = p.communicate()
  147. # # check for potential oops
  148. # self.assertNotIn('end trace', stdout)