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