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. 12import sys 13import os 14 15from qemu.machine import QEMUMachine 16from qemu_test 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 53 vm.cmd('qom-list-types', implements=implements)] 54 55def get_pci_interfaces(vm, devtype): 56 interfaces = ('pci-express-device', 'conventional-pci-device') 57 return [i for i in interfaces if devtype_implements(vm, devtype, i)] 58 59class VirtioVersionCheck(QemuSystemTest): 60 """ 61 Check if virtio-version-specific device types result in the 62 same device tree created by `disable-modern` and 63 `disable-legacy`. 64 """ 65 66 # just in case there are failures, show larger diff: 67 maxDiff = 4096 68 69 def run_device(self, devtype, opts=None, machine='pc'): 70 """ 71 Run QEMU with `-device DEVTYPE`, return device info from `query-pci` 72 """ 73 with QEMUMachine(self.qemu_bin) as vm: 74 vm.set_machine(machine) 75 if opts: 76 devtype += ',' + opts 77 vm.add_args('-device', '%s,id=devfortest' % (devtype)) 78 vm.add_args('-S') 79 vm.launch() 80 81 pcibuses = vm.cmd('query-pci') 82 alldevs = [dev for bus in pcibuses for dev in bus['devices']] 83 devfortest = [dev for dev in alldevs 84 if dev['qdev_id'] == 'devfortest'] 85 return devfortest[0], get_pci_interfaces(vm, devtype) 86 87 88 def assert_devids(self, dev, devid, non_transitional=False): 89 self.assertEqual(dev['id']['vendor'], PCI_VENDOR_ID_REDHAT_QUMRANET) 90 self.assertEqual(dev['id']['device'], devid) 91 if non_transitional: 92 self.assertTrue(0x1040 <= dev['id']['device'] <= 0x107f) 93 self.assertGreaterEqual(dev['id']['subsystem'], 0x40) 94 95 def check_all_variants(self, qemu_devtype, virtio_devid): 96 """Check if a virtio device type and its variants behave as expected""" 97 # Force modern mode: 98 dev_modern, _ = self.run_device(qemu_devtype, 99 'disable-modern=off,disable-legacy=on') 100 self.assert_devids(dev_modern, pci_modern_device_id(virtio_devid), 101 non_transitional=True) 102 103 # <prefix>-non-transitional device types should be 100% equivalent to 104 # <prefix>,disable-modern=off,disable-legacy=on 105 dev_1_0, nt_ifaces = self.run_device('%s-non-transitional' % (qemu_devtype)) 106 self.assertEqual(dev_modern, dev_1_0) 107 108 # Force transitional mode: 109 dev_trans, _ = self.run_device(qemu_devtype, 110 'disable-modern=off,disable-legacy=off') 111 self.assert_devids(dev_trans, PCI_LEGACY_DEVICE_IDS[virtio_devid]) 112 113 # Force legacy mode: 114 dev_legacy, _ = self.run_device(qemu_devtype, 115 'disable-modern=on,disable-legacy=off') 116 self.assert_devids(dev_legacy, PCI_LEGACY_DEVICE_IDS[virtio_devid]) 117 118 # No options: default to transitional on PC machine-type: 119 no_opts_pc, generic_ifaces = self.run_device(qemu_devtype) 120 self.assertEqual(dev_trans, no_opts_pc) 121 122 #TODO: check if plugging on a PCI Express bus will make the 123 # device non-transitional 124 #no_opts_q35 = self.run_device(qemu_devtype, machine='q35') 125 #self.assertEqual(dev_modern, no_opts_q35) 126 127 # <prefix>-transitional device types should be 100% equivalent to 128 # <prefix>,disable-modern=off,disable-legacy=off 129 dev_trans, trans_ifaces = self.run_device('%s-transitional' % (qemu_devtype)) 130 self.assertEqual(dev_trans, dev_trans) 131 132 # ensure the interface information is correct: 133 self.assertIn('conventional-pci-device', generic_ifaces) 134 self.assertIn('pci-express-device', generic_ifaces) 135 136 self.assertIn('conventional-pci-device', nt_ifaces) 137 self.assertIn('pci-express-device', nt_ifaces) 138 139 self.assertIn('conventional-pci-device', trans_ifaces) 140 self.assertNotIn('pci-express-device', trans_ifaces) 141 142 143 def test_conventional_devs(self): 144 self.check_all_variants('virtio-net-pci', VIRTIO_NET) 145 # virtio-blk requires 'driver' parameter 146 #self.check_all_variants('virtio-blk-pci', VIRTIO_BLOCK) 147 self.check_all_variants('virtio-serial-pci', VIRTIO_CONSOLE) 148 self.check_all_variants('virtio-rng-pci', VIRTIO_RNG) 149 self.check_all_variants('virtio-balloon-pci', VIRTIO_BALLOON) 150 self.check_all_variants('virtio-scsi-pci', VIRTIO_SCSI) 151 # virtio-9p requires 'fsdev' parameter 152 #self.check_all_variants('virtio-9p-pci', VIRTIO_9P) 153 154 def check_modern_only(self, qemu_devtype, virtio_devid): 155 """Check if a modern-only virtio device type behaves as expected""" 156 # Force modern mode: 157 dev_modern, _ = self.run_device(qemu_devtype, 158 'disable-modern=off,disable-legacy=on') 159 self.assert_devids(dev_modern, pci_modern_device_id(virtio_devid), 160 non_transitional=True) 161 162 # No options: should be modern anyway 163 dev_no_opts, ifaces = self.run_device(qemu_devtype) 164 self.assertEqual(dev_modern, dev_no_opts) 165 166 self.assertIn('conventional-pci-device', ifaces) 167 self.assertIn('pci-express-device', ifaces) 168 169 def test_modern_only_devs(self): 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