1""" 2Check compatibility of virtio device types 3""" 4# Copyright (c) 2018 Red Hat, Inc. 5# 6# Author: 7# Eduardo Habkost <ehabkost@redhat.com> 8# 9# This work is licensed under the terms of the GNU GPL, version 2 or 10# later. See the COPYING file in the top-level directory. 11import sys 12import os 13 14sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) 15from qemu.machine import QEMUMachine 16from avocado_qemu import QemuSystemTest 17 18# Virtio Device IDs: 19VIRTIO_NET = 1 20VIRTIO_BLOCK = 2 21VIRTIO_CONSOLE = 3 22VIRTIO_RNG = 4 23VIRTIO_BALLOON = 5 24VIRTIO_RPMSG = 7 25VIRTIO_SCSI = 8 26VIRTIO_9P = 9 27VIRTIO_RPROC_SERIAL = 11 28VIRTIO_CAIF = 12 29VIRTIO_GPU = 16 30VIRTIO_INPUT = 18 31VIRTIO_VSOCK = 19 32VIRTIO_CRYPTO = 20 33 34PCI_VENDOR_ID_REDHAT_QUMRANET = 0x1af4 35 36# Device IDs for legacy/transitional devices: 37PCI_LEGACY_DEVICE_IDS = { 38 VIRTIO_NET: 0x1000, 39 VIRTIO_BLOCK: 0x1001, 40 VIRTIO_BALLOON: 0x1002, 41 VIRTIO_CONSOLE: 0x1003, 42 VIRTIO_SCSI: 0x1004, 43 VIRTIO_RNG: 0x1005, 44 VIRTIO_9P: 0x1009, 45 VIRTIO_VSOCK: 0x1012, 46} 47 48def pci_modern_device_id(virtio_devid): 49 return virtio_devid + 0x1040 50 51def devtype_implements(vm, devtype, implements): 52 return devtype in [d['name'] for d in vm.command('qom-list-types', implements=implements)] 53 54def get_pci_interfaces(vm, devtype): 55 interfaces = ('pci-express-device', 'conventional-pci-device') 56 return [i for i in interfaces if devtype_implements(vm, devtype, i)] 57 58class VirtioVersionCheck(QemuSystemTest): 59 """ 60 Check if virtio-version-specific device types result in the 61 same device tree created by `disable-modern` and 62 `disable-legacy`. 63 64 :avocado: tags=arch:x86_64 65 """ 66 67 # just in case there are failures, show larger diff: 68 maxDiff = 4096 69 70 def run_device(self, devtype, opts=None, machine='pc'): 71 """ 72 Run QEMU with `-device DEVTYPE`, return device info from `query-pci` 73 """ 74 with QEMUMachine(self.qemu_bin) as vm: 75 vm.set_machine(machine) 76 if opts: 77 devtype += ',' + opts 78 vm.add_args('-device', '%s,id=devfortest' % (devtype)) 79 vm.add_args('-S') 80 vm.launch() 81 82 pcibuses = vm.command('query-pci') 83 alldevs = [dev for bus in pcibuses for dev in bus['devices']] 84 devfortest = [dev for dev in alldevs 85 if dev['qdev_id'] == 'devfortest'] 86 return devfortest[0], get_pci_interfaces(vm, devtype) 87 88 89 def assert_devids(self, dev, devid, non_transitional=False): 90 self.assertEqual(dev['id']['vendor'], PCI_VENDOR_ID_REDHAT_QUMRANET) 91 self.assertEqual(dev['id']['device'], devid) 92 if non_transitional: 93 self.assertTrue(0x1040 <= dev['id']['device'] <= 0x107f) 94 self.assertGreaterEqual(dev['id']['subsystem'], 0x40) 95 96 def check_all_variants(self, qemu_devtype, virtio_devid): 97 """Check if a virtio device type and its variants behave as expected""" 98 # Force modern mode: 99 dev_modern, _ = self.run_device(qemu_devtype, 100 'disable-modern=off,disable-legacy=on') 101 self.assert_devids(dev_modern, pci_modern_device_id(virtio_devid), 102 non_transitional=True) 103 104 # <prefix>-non-transitional device types should be 100% equivalent to 105 # <prefix>,disable-modern=off,disable-legacy=on 106 dev_1_0, nt_ifaces = self.run_device('%s-non-transitional' % (qemu_devtype)) 107 self.assertEqual(dev_modern, dev_1_0) 108 109 # Force transitional mode: 110 dev_trans, _ = self.run_device(qemu_devtype, 111 'disable-modern=off,disable-legacy=off') 112 self.assert_devids(dev_trans, PCI_LEGACY_DEVICE_IDS[virtio_devid]) 113 114 # Force legacy mode: 115 dev_legacy, _ = self.run_device(qemu_devtype, 116 'disable-modern=on,disable-legacy=off') 117 self.assert_devids(dev_legacy, PCI_LEGACY_DEVICE_IDS[virtio_devid]) 118 119 # No options: default to transitional on PC machine-type: 120 no_opts_pc, generic_ifaces = self.run_device(qemu_devtype) 121 self.assertEqual(dev_trans, no_opts_pc) 122 123 #TODO: check if plugging on a PCI Express bus will make the 124 # device non-transitional 125 #no_opts_q35 = self.run_device(qemu_devtype, machine='q35') 126 #self.assertEqual(dev_modern, no_opts_q35) 127 128 # <prefix>-transitional device types should be 100% equivalent to 129 # <prefix>,disable-modern=off,disable-legacy=off 130 dev_trans, trans_ifaces = self.run_device('%s-transitional' % (qemu_devtype)) 131 self.assertEqual(dev_trans, dev_trans) 132 133 # ensure the interface information is correct: 134 self.assertIn('conventional-pci-device', generic_ifaces) 135 self.assertIn('pci-express-device', generic_ifaces) 136 137 self.assertIn('conventional-pci-device', nt_ifaces) 138 self.assertIn('pci-express-device', nt_ifaces) 139 140 self.assertIn('conventional-pci-device', trans_ifaces) 141 self.assertNotIn('pci-express-device', trans_ifaces) 142 143 144 def test_conventional_devs(self): 145 self.check_all_variants('virtio-net-pci', VIRTIO_NET) 146 # virtio-blk requires 'driver' parameter 147 #self.check_all_variants('virtio-blk-pci', VIRTIO_BLOCK) 148 self.check_all_variants('virtio-serial-pci', VIRTIO_CONSOLE) 149 self.check_all_variants('virtio-rng-pci', VIRTIO_RNG) 150 self.check_all_variants('virtio-balloon-pci', VIRTIO_BALLOON) 151 self.check_all_variants('virtio-scsi-pci', VIRTIO_SCSI) 152 # virtio-9p requires 'fsdev' parameter 153 #self.check_all_variants('virtio-9p-pci', VIRTIO_9P) 154 155 def check_modern_only(self, qemu_devtype, virtio_devid): 156 """Check if a modern-only virtio device type behaves as expected""" 157 # Force modern mode: 158 dev_modern, _ = self.run_device(qemu_devtype, 159 'disable-modern=off,disable-legacy=on') 160 self.assert_devids(dev_modern, pci_modern_device_id(virtio_devid), 161 non_transitional=True) 162 163 # No options: should be modern anyway 164 dev_no_opts, ifaces = self.run_device(qemu_devtype) 165 self.assertEqual(dev_modern, dev_no_opts) 166 167 self.assertIn('conventional-pci-device', ifaces) 168 self.assertIn('pci-express-device', ifaces) 169 170 def test_modern_only_devs(self): 171 self.check_modern_only('virtio-vga', VIRTIO_GPU) 172 self.check_modern_only('virtio-gpu-pci', VIRTIO_GPU) 173 self.check_modern_only('virtio-mouse-pci', VIRTIO_INPUT) 174 self.check_modern_only('virtio-tablet-pci', VIRTIO_INPUT) 175 self.check_modern_only('virtio-keyboard-pci', VIRTIO_INPUT) 176