]>
Commit | Line | Data |
---|---|---|
f6e501a2 EH |
1 | """ |
2 | Check compatibility of virtio device types | |
3 | """ | |
4 | # Copyright (c) 2018 Red Hat, Inc. | |
5 | # | |
6 | # Author: | |
7 | # Eduardo Habkost <[email protected]> | |
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. | |
11 | import sys | |
12 | import os | |
13 | ||
8f8fd9ed | 14 | sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) |
f6e501a2 EH |
15 | from qemu import QEMUMachine |
16 | from avocado_qemu import Test | |
17 | ||
18 | # Virtio Device IDs: | |
19 | VIRTIO_NET = 1 | |
20 | VIRTIO_BLOCK = 2 | |
21 | VIRTIO_CONSOLE = 3 | |
22 | VIRTIO_RNG = 4 | |
23 | VIRTIO_BALLOON = 5 | |
24 | VIRTIO_RPMSG = 7 | |
25 | VIRTIO_SCSI = 8 | |
26 | VIRTIO_9P = 9 | |
27 | VIRTIO_RPROC_SERIAL = 11 | |
28 | VIRTIO_CAIF = 12 | |
29 | VIRTIO_GPU = 16 | |
30 | VIRTIO_INPUT = 18 | |
31 | VIRTIO_VSOCK = 19 | |
32 | VIRTIO_CRYPTO = 20 | |
33 | ||
34 | PCI_VENDOR_ID_REDHAT_QUMRANET = 0x1af4 | |
35 | ||
36 | # Device IDs for legacy/transitional devices: | |
37 | PCI_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 | ||
48 | def pci_modern_device_id(virtio_devid): | |
49 | return virtio_devid + 0x1040 | |
50 | ||
51 | def devtype_implements(vm, devtype, implements): | |
52 | return devtype in [d['name'] for d in vm.command('qom-list-types', implements=implements)] | |
53 | ||
54 | def 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 | ||
58 | class VirtioVersionCheck(Test): | |
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 | ||
f6e501a2 EH |
64 | :avocado: tags=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) |