| 1 | #!/usr/bin/env python3 |
| 2 | # |
| 3 | # SPDX-License-Identifier: GPL-2.0-or-later |
| 4 | # |
| 5 | # SMMUv3 Functional tests |
| 6 | # |
| 7 | # Copyright (c) 2021 Red Hat, Inc. |
| 8 | # |
| 9 | # Author: |
| 10 | # Eric Auger <eric.auger@redhat.com> |
| 11 | # |
| 12 | # This work is licensed under the terms of the GNU GPL, version 2 or |
| 13 | # later. See the COPYING file in the top-level directory. |
| 14 | |
| 15 | import os |
| 16 | import time |
| 17 | |
| 18 | from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern |
| 19 | from qemu_test import BUILD_DIR |
| 20 | from qemu.utils import kvm_available, hvf_available |
| 21 | |
| 22 | |
| 23 | class SMMU(LinuxKernelTest): |
| 24 | |
| 25 | default_kernel_params = ('earlyprintk=pl011,0x9000000 no_timer_check ' |
| 26 | 'printk.time=1 rd_NO_PLYMOUTH net.ifnames=0 ' |
| 27 | 'console=ttyAMA0 rd.rescue') |
| 28 | IOMMU_ADDON = ',iommu_platform=on,disable-modern=off,disable-legacy=on' |
| 29 | kernel_path = None |
| 30 | initrd_path = None |
| 31 | kernel_params = None |
| 32 | |
| 33 | GUEST_PORT = 8080 |
| 34 | |
| 35 | def set_up_boot(self, path): |
| 36 | self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,' + |
| 37 | 'drive=drv0,id=virtio-disk0,bootindex=1,' |
| 38 | 'werror=stop,rerror=stop' + self.IOMMU_ADDON) |
| 39 | self.vm.add_args('-drive', |
| 40 | f'file={path},if=none,cache=writethrough,id=drv0,snapshot=on') |
| 41 | |
| 42 | self.vm.add_args('-netdev', |
| 43 | 'user,id=n1,hostfwd=tcp:127.0.0.1:0-:%d' % |
| 44 | self.GUEST_PORT) |
| 45 | self.vm.add_args('-device', 'virtio-net,netdev=n1' + self.IOMMU_ADDON) |
| 46 | |
| 47 | def common_vm_setup(self, kernel, initrd, disk): |
| 48 | if hvf_available(self.qemu_bin): |
| 49 | accel = "hvf" |
| 50 | elif kvm_available(self.qemu_bin): |
| 51 | accel = "kvm" |
| 52 | else: |
| 53 | accel = None # for keeping pylint happy |
| 54 | self.skipTest("Neither HVF nor KVM accelerator is available") |
| 55 | self.require_accelerator(accel) |
| 56 | self.require_netdev('user') |
| 57 | self.set_machine("virt") |
| 58 | self.vm.add_args('-m', '1G') |
| 59 | self.vm.add_args("-accel", accel) |
| 60 | self.vm.add_args("-cpu", "host") |
| 61 | self.vm.add_args("-machine", "iommu=smmuv3") |
| 62 | self.vm.add_args("-d", "guest_errors") |
| 63 | self.vm.add_args('-bios', os.path.join(BUILD_DIR, 'pc-bios', |
| 64 | 'edk2-aarch64-code.fd')) |
| 65 | self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0') |
| 66 | self.vm.add_args('-object', |
| 67 | 'rng-random,id=rng0,filename=/dev/urandom') |
| 68 | |
| 69 | self.kernel_path = kernel.fetch() |
| 70 | self.initrd_path = initrd.fetch() |
| 71 | self.set_up_boot(disk.fetch()) |
| 72 | |
| 73 | def run_and_check(self, filename, hashsum): |
| 74 | self.vm.add_args('-initrd', self.initrd_path) |
| 75 | self.vm.add_args('-append', self.kernel_params) |
| 76 | self.launch_kernel(self.kernel_path, initrd=self.initrd_path, |
| 77 | wait_for='attach it to a bug report.') |
| 78 | prompt = '# ' |
| 79 | # Fedora 33 requires 'return' to be pressed to enter the shell. |
| 80 | # There seems to be a small race between detecting the previous ':' |
| 81 | # and sending the newline, so we need to add a small delay here. |
| 82 | self.wait_for_console_pattern(':') |
| 83 | time.sleep(0.2) |
| 84 | exec_command_and_wait_for_pattern(self, '\n', prompt) |
| 85 | exec_command_and_wait_for_pattern(self, 'cat /proc/cmdline', |
| 86 | self.kernel_params) |
| 87 | |
| 88 | # Checking for SMMU enablement: |
| 89 | self.log.info("Checking whether SMMU has been enabled...") |
| 90 | exec_command_and_wait_for_pattern(self, 'dmesg | grep smmu', |
| 91 | 'arm-smmu-v3') |
| 92 | self.wait_for_console_pattern(prompt) |
| 93 | exec_command_and_wait_for_pattern(self, |
| 94 | 'find /sys/kernel/iommu_groups/ -type l', |
| 95 | 'devices/0000:00:') |
| 96 | self.wait_for_console_pattern(prompt) |
| 97 | |
| 98 | # Copy a file (checked later), umount afterwards to drop disk cache: |
| 99 | self.log.info("Checking hard disk...") |
| 100 | exec_command_and_wait_for_pattern(self, |
| 101 | "while ! (dmesg -c | grep vda:) ; do sleep 1 ; done", |
| 102 | "vda2") |
| 103 | exec_command_and_wait_for_pattern(self, 'mount /dev/vda2 /sysroot', |
| 104 | 'mounted filesystem') |
| 105 | exec_command_and_wait_for_pattern(self, 'cp /bin/vi /sysroot/root/vi', |
| 106 | prompt) |
| 107 | exec_command_and_wait_for_pattern(self, 'umount /sysroot', prompt) |
| 108 | # Switch from initrd to the cloud image filesystem: |
| 109 | exec_command_and_wait_for_pattern(self, 'mount /dev/vda2 /sysroot', |
| 110 | prompt) |
| 111 | exec_command_and_wait_for_pattern(self, |
| 112 | ('for d in dev proc sys run ; do ' |
| 113 | 'mount -o bind /$d /sysroot/$d ; done'), prompt) |
| 114 | exec_command_and_wait_for_pattern(self, 'chroot /sysroot', prompt) |
| 115 | # Check files on the hard disk: |
| 116 | exec_command_and_wait_for_pattern(self, |
| 117 | ('if diff -q /root/vi /usr/bin/vi ; then echo "file" "ok" ; ' |
| 118 | 'else echo "files differ"; fi'), 'file ok') |
| 119 | self.wait_for_console_pattern(prompt) |
| 120 | exec_command_and_wait_for_pattern(self, f'sha256sum {filename}', |
| 121 | hashsum) |
| 122 | |
| 123 | # Check virtio-net via HTTP: |
| 124 | exec_command_and_wait_for_pattern(self, 'dhclient eth0', prompt) |
| 125 | self.check_http_download(filename, hashsum, self.GUEST_PORT) |
| 126 | |
| 127 | |
| 128 | # 5.3 kernel without RIL # |
| 129 | |
| 130 | ASSET_KERNEL_F31 = Asset( |
| 131 | ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' |
| 132 | 'releases/31/Server/aarch64/os/images/pxeboot/vmlinuz'), |
| 133 | '3ae07fcafbfc8e4abeb693035a74fe10698faae15e9ccd48882a9167800c1527') |
| 134 | |
| 135 | ASSET_INITRD_F31 = Asset( |
| 136 | ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' |
| 137 | 'releases/31/Server/aarch64/os/images/pxeboot/initrd.img'), |
| 138 | '9f3146b28bc531c689f3c5f114cb74e4bd7bd548e0ba19fa77921d8bd256755a') |
| 139 | |
| 140 | ASSET_DISK_F31 = Asset( |
| 141 | ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' |
| 142 | '/31/Cloud/aarch64/images/Fedora-Cloud-Base-31-1.9.aarch64.qcow2'), |
| 143 | '1e18d9c0cf734940c4b5d5ec592facaed2af0ad0329383d5639c997fdf16fe49') |
| 144 | |
| 145 | F31_FILENAME = '/boot/initramfs-5.3.7-301.fc31.aarch64.img' |
| 146 | F31_HSUM = '1a4beec6607d94df73d9dd1b4985c9c23dd0fdcf4e6ca1351d477f190df7bef9' |
| 147 | |
| 148 | def test_smmu_noril(self): |
| 149 | self.common_vm_setup(self.ASSET_KERNEL_F31, self.ASSET_INITRD_F31, |
| 150 | self.ASSET_DISK_F31) |
| 151 | self.kernel_params = self.default_kernel_params |
| 152 | self.run_and_check(self.F31_FILENAME, self.F31_HSUM) |
| 153 | |
| 154 | def test_smmu_noril_passthrough(self): |
| 155 | self.common_vm_setup(self.ASSET_KERNEL_F31, self.ASSET_INITRD_F31, |
| 156 | self.ASSET_DISK_F31) |
| 157 | self.kernel_params = (self.default_kernel_params + |
| 158 | ' iommu.passthrough=on') |
| 159 | self.run_and_check(self.F31_FILENAME, self.F31_HSUM) |
| 160 | |
| 161 | def test_smmu_noril_nostrict(self): |
| 162 | self.common_vm_setup(self.ASSET_KERNEL_F31, self.ASSET_INITRD_F31, |
| 163 | self.ASSET_DISK_F31) |
| 164 | self.kernel_params = (self.default_kernel_params + |
| 165 | ' iommu.strict=0') |
| 166 | self.run_and_check(self.F31_FILENAME, self.F31_HSUM) |
| 167 | |
| 168 | |
| 169 | # 5.8 kernel featuring range invalidation |
| 170 | # >= v5.7 kernel |
| 171 | |
| 172 | ASSET_KERNEL_F33 = Asset( |
| 173 | ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' |
| 174 | 'releases/33/Server/aarch64/os/images/pxeboot/vmlinuz'), |
| 175 | 'd8b1e6f7241f339d8e7609c456cf0461ffa4583ed07e0b55c7d1d8a0c154aa89') |
| 176 | |
| 177 | ASSET_INITRD_F33 = Asset( |
| 178 | ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' |
| 179 | 'releases/33/Server/aarch64/os/images/pxeboot/initrd.img'), |
| 180 | '92513f55295c2c16a777f7b6c35ccd70a438e9e1e40b6ba39e0e60900615b3df') |
| 181 | |
| 182 | ASSET_DISK_F33 = Asset( |
| 183 | ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' |
| 184 | '/33/Cloud/aarch64/images/Fedora-Cloud-Base-33-1.2.aarch64.qcow2'), |
| 185 | 'e7f75cdfd523fe5ac2ca9eeece68edc1a81f386a17f969c1d1c7c87031008a6b') |
| 186 | |
| 187 | F33_FILENAME = '/boot/initramfs-5.8.15-301.fc33.aarch64.img' |
| 188 | F33_HSUM = '079cfad0caa82e84c8ca1fb0897a4999dd769f262216099f518619e807a550d9' |
| 189 | |
| 190 | def test_smmu_ril(self): |
| 191 | self.common_vm_setup(self.ASSET_KERNEL_F33, self.ASSET_INITRD_F33, |
| 192 | self.ASSET_DISK_F33) |
| 193 | self.kernel_params = self.default_kernel_params |
| 194 | self.run_and_check(self.F33_FILENAME, self.F33_HSUM) |
| 195 | |
| 196 | def test_smmu_ril_passthrough(self): |
| 197 | self.common_vm_setup(self.ASSET_KERNEL_F33, self.ASSET_INITRD_F33, |
| 198 | self.ASSET_DISK_F33) |
| 199 | self.kernel_params = (self.default_kernel_params + |
| 200 | ' iommu.passthrough=on') |
| 201 | self.run_and_check(self.F33_FILENAME, self.F33_HSUM) |
| 202 | |
| 203 | def test_smmu_ril_nostrict(self): |
| 204 | self.common_vm_setup(self.ASSET_KERNEL_F33, self.ASSET_INITRD_F33, |
| 205 | self.ASSET_DISK_F33) |
| 206 | self.kernel_params = (self.default_kernel_params + |
| 207 | ' iommu.strict=0') |
| 208 | self.run_and_check(self.F33_FILENAME, self.F33_HSUM) |
| 209 | |
| 210 | |
| 211 | if __name__ == '__main__': |
| 212 | LinuxKernelTest.main() |