aboutsummaryrefslogtreecommitdiff
path: root/tests/avocado
diff options
context:
space:
mode:
Diffstat (limited to 'tests/avocado')
-rw-r--r--tests/avocado/README.rst10
-rw-r--r--tests/avocado/acpi-bits.py409
-rw-r--r--tests/avocado/acpi-bits/bits-config/bits-cfg.txt18
-rw-r--r--tests/avocado/acpi-bits/bits-tests/smbios.py22434
-rw-r--r--tests/avocado/acpi-bits/bits-tests/smilatency.py2107
-rw-r--r--tests/avocado/acpi-bits/bits-tests/testacpi.py2287
-rw-r--r--tests/avocado/acpi-bits/bits-tests/testcpuid.py287
-rw-r--r--tests/avocado/avocado_qemu/__init__.py681
-rw-r--r--tests/avocado/boot_linux.py131
-rw-r--r--tests/avocado/boot_linux_console.py1547
-rw-r--r--tests/avocado/boot_xen.py116
-rw-r--r--tests/avocado/cpu_queries.py35
-rw-r--r--tests/avocado/empty_cpu_model.py19
-rw-r--r--tests/avocado/hotplug_cpu.py37
-rw-r--r--tests/avocado/info_usernet.py33
-rw-r--r--tests/avocado/intel_iommu.py123
-rw-r--r--tests/avocado/kvm_xen_guest.py171
-rw-r--r--tests/avocado/linux_initrd.py92
-rw-r--r--tests/avocado/linux_ssh_mips_malta.py205
-rw-r--r--tests/avocado/load_bflt.py54
-rw-r--r--tests/avocado/machine_aarch64_sbsaref.py238
-rw-r--r--tests/avocado/machine_aarch64_virt.py146
-rw-r--r--tests/avocado/machine_arm_canona1100.py35
-rw-r--r--tests/avocado/machine_arm_integratorcp.py99
-rw-r--r--tests/avocado/machine_arm_n8x0.py49
-rw-r--r--tests/avocado/machine_aspeed.py377
-rw-r--r--tests/avocado/machine_avr6.py50
-rw-r--r--tests/avocado/machine_loongarch.py58
-rw-r--r--tests/avocado/machine_m68k_nextcube.py70
-rw-r--r--tests/avocado/machine_microblaze.py61
-rw-r--r--tests/avocado/machine_mips_fuloong2e.py42
-rw-r--r--tests/avocado/machine_mips_loongson3v.py39
-rw-r--r--tests/avocado/machine_mips_malta.py164
-rw-r--r--tests/avocado/machine_rx_gdbsim.py77
-rw-r--r--tests/avocado/machine_s390_ccw_virtio.py277
-rw-r--r--tests/avocado/machine_sparc64_sun4u.py36
-rw-r--r--tests/avocado/machine_sparc_leon3.py37
-rw-r--r--tests/avocado/mem-addr-space-check.py356
-rw-r--r--tests/avocado/migration.py135
-rw-r--r--tests/avocado/multiprocess.py102
-rw-r--r--tests/avocado/netdev-ethtool.py101
-rw-r--r--tests/avocado/pc_cpu_hotplug_props.py35
-rw-r--r--tests/avocado/ppc_405.py36
-rw-r--r--tests/avocado/ppc_74xx.py136
-rw-r--r--tests/avocado/ppc_amiga.py38
-rw-r--r--tests/avocado/ppc_bamboo.py42
-rw-r--r--tests/avocado/ppc_hv_tests.py206
-rw-r--r--tests/avocado/ppc_mpc8544ds.py34
-rw-r--r--tests/avocado/ppc_powernv.py102
-rw-r--r--tests/avocado/ppc_prep_40p.py85
-rw-r--r--tests/avocado/ppc_pseries.py110
-rw-r--r--tests/avocado/ppc_virtex_ml507.py36
-rw-r--r--tests/avocado/replay_kernel.py550
-rw-r--r--tests/avocado/replay_linux.py196
-rw-r--r--tests/avocado/reverse_debugging.py276
-rw-r--r--tests/avocado/riscv_opensbi.py63
-rw-r--r--tests/avocado/s390_topology.py439
-rw-r--r--tests/avocado/smmu.py139
-rw-r--r--tests/avocado/tcg_plugins.py155
-rw-r--r--tests/avocado/tesseract_utils.py46
-rw-r--r--tests/avocado/tuxrun_baselines.py620
-rw-r--r--tests/avocado/version.py25
-rw-r--r--tests/avocado/virtio-gpu.py157
-rw-r--r--tests/avocado/virtio_check_params.py143
-rw-r--r--tests/avocado/virtio_version.py175
-rw-r--r--tests/avocado/virtiofs_submounts.py.data/cleanup.sh46
-rw-r--r--tests/avocado/virtiofs_submounts.py.data/guest-cleanup.sh30
-rw-r--r--tests/avocado/virtiofs_submounts.py.data/guest.sh138
-rw-r--r--tests/avocado/virtiofs_submounts.py.data/host.sh127
-rw-r--r--tests/avocado/vnc.py115
-rw-r--r--tests/avocado/x86_cpu_model_versions.py362
71 files changed, 13807 insertions, 0 deletions
diff --git a/tests/avocado/README.rst b/tests/avocado/README.rst
new file mode 100644
index 0000000000..94488371bb
--- /dev/null
+++ b/tests/avocado/README.rst
@@ -0,0 +1,10 @@
+=============================================
+Integration tests using the Avocado Framework
+=============================================
+
+This directory contains integration tests. They're usually higher
+level, and may interact with external resources and with various
+guest operating systems.
+
+For more information, please refer to ``docs/devel/testing.rst``,
+section "Integration tests using the Avocado Framework".
diff --git a/tests/avocado/acpi-bits.py b/tests/avocado/acpi-bits.py
new file mode 100644
index 0000000000..efe4f52ee0
--- /dev/null
+++ b/tests/avocado/acpi-bits.py
@@ -0,0 +1,409 @@
+#!/usr/bin/env python3
+# group: rw quick
+# Exercise QEMU generated ACPI/SMBIOS tables using biosbits,
+# https://biosbits.org/
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#
+# Author:
+# Ani Sinha <anisinha@redhat.com>
+
+# pylint: disable=invalid-name
+# pylint: disable=consider-using-f-string
+
+"""
+This is QEMU ACPI/SMBIOS avocado tests using biosbits.
+Biosbits is available originally at https://biosbits.org/.
+This test uses a fork of the upstream bits and has numerous fixes
+including an upgraded acpica. The fork is located here:
+https://gitlab.com/qemu-project/biosbits-bits .
+"""
+
+import logging
+import os
+import platform
+import re
+import shutil
+import subprocess
+import tarfile
+import tempfile
+import time
+import zipfile
+from typing import (
+ List,
+ Optional,
+ Sequence,
+)
+from qemu.machine import QEMUMachine
+from avocado import skipIf
+from avocado.utils import datadrainer as drainer
+from avocado_qemu import QemuBaseTest
+
+deps = ["xorriso", "mformat"] # dependent tools needed in the test setup/box.
+supported_platforms = ['x86_64'] # supported test platforms.
+
+# default timeout of 120 secs is sometimes not enough for bits test.
+BITS_TIMEOUT = 200
+
+def which(tool):
+ """ looks up the full path for @tool, returns None if not found
+ or if @tool does not have executable permissions.
+ """
+ paths=os.getenv('PATH')
+ for p in paths.split(os.path.pathsep):
+ p = os.path.join(p, tool)
+ if os.path.exists(p) and os.access(p, os.X_OK):
+ return p
+ return None
+
+def missing_deps():
+ """ returns True if any of the test dependent tools are absent.
+ """
+ for dep in deps:
+ if which(dep) is None:
+ return True
+ return False
+
+def supported_platform():
+ """ checks if the test is running on a supported platform.
+ """
+ return platform.machine() in supported_platforms
+
+class QEMUBitsMachine(QEMUMachine): # pylint: disable=too-few-public-methods
+ """
+ A QEMU VM, with isa-debugcon enabled and bits iso passed
+ using -cdrom to QEMU commandline.
+
+ """
+ def __init__(self,
+ binary: str,
+ args: Sequence[str] = (),
+ wrapper: Sequence[str] = (),
+ name: Optional[str] = None,
+ base_temp_dir: str = "/var/tmp",
+ debugcon_log: str = "debugcon-log.txt",
+ debugcon_addr: str = "0x403",
+ qmp_timer: Optional[float] = None):
+ # pylint: disable=too-many-arguments
+
+ if name is None:
+ name = "qemu-bits-%d" % os.getpid()
+ super().__init__(binary, args, wrapper=wrapper, name=name,
+ base_temp_dir=base_temp_dir,
+ qmp_timer=qmp_timer)
+ self.debugcon_log = debugcon_log
+ self.debugcon_addr = debugcon_addr
+ self.base_temp_dir = base_temp_dir
+
+ @property
+ def _base_args(self) -> List[str]:
+ args = super()._base_args
+ args.extend([
+ '-chardev',
+ 'file,path=%s,id=debugcon' %os.path.join(self.base_temp_dir,
+ self.debugcon_log),
+ '-device',
+ 'isa-debugcon,iobase=%s,chardev=debugcon' %self.debugcon_addr,
+ ])
+ return args
+
+ def base_args(self):
+ """return the base argument to QEMU binary"""
+ return self._base_args
+
+@skipIf(not supported_platform() or missing_deps(),
+ 'unsupported platform or dependencies (%s) not installed' \
+ % ','.join(deps))
+class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes
+ """
+ ACPI and SMBIOS tests using biosbits.
+
+ :avocado: tags=arch:x86_64
+ :avocado: tags=acpi
+
+ """
+ # in slower systems the test can take as long as 3 minutes to complete.
+ timeout = BITS_TIMEOUT
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self._vm = None
+ self._workDir = None
+ self._baseDir = None
+
+ # following are some standard configuration constants
+ self._bitsInternalVer = 2020 # gitlab CI does shallow clones of depth 20
+ self._bitsCommitHash = 'c7920d2b' # commit hash must match
+ # the artifact tag below
+ self._bitsTag = "qemu-bits-10262023" # this is the latest bits
+ # release as of today.
+ self._bitsArtSHA1Hash = 'b22cdfcfc7453875297d06d626f5474ee36a343f'
+ self._bitsArtURL = ("https://gitlab.com/qemu-project/"
+ "biosbits-bits/-/jobs/artifacts/%s/"
+ "download?job=qemu-bits-build" %self._bitsTag)
+ self._debugcon_addr = '0x403'
+ self._debugcon_log = 'debugcon-log.txt'
+ logging.basicConfig(level=logging.INFO)
+ self.logger = logging.getLogger('acpi-bits')
+
+ def _print_log(self, log):
+ self.logger.info('\nlogs from biosbits follows:')
+ self.logger.info('==========================================\n')
+ self.logger.info(log)
+ self.logger.info('==========================================\n')
+
+ def copy_bits_config(self):
+ """ copies the bios bits config file into bits.
+ """
+ config_file = 'bits-cfg.txt'
+ bits_config_dir = os.path.join(self._baseDir, 'acpi-bits',
+ 'bits-config')
+ target_config_dir = os.path.join(self._workDir,
+ 'bits-%d' %self._bitsInternalVer,
+ 'boot')
+ self.assertTrue(os.path.exists(bits_config_dir))
+ self.assertTrue(os.path.exists(target_config_dir))
+ self.assertTrue(os.access(os.path.join(bits_config_dir,
+ config_file), os.R_OK))
+ shutil.copy2(os.path.join(bits_config_dir, config_file),
+ target_config_dir)
+ self.logger.info('copied config file %s to %s',
+ config_file, target_config_dir)
+
+ def copy_test_scripts(self):
+ """copies the python test scripts into bits. """
+
+ bits_test_dir = os.path.join(self._baseDir, 'acpi-bits',
+ 'bits-tests')
+ target_test_dir = os.path.join(self._workDir,
+ 'bits-%d' %self._bitsInternalVer,
+ 'boot', 'python')
+
+ self.assertTrue(os.path.exists(bits_test_dir))
+ self.assertTrue(os.path.exists(target_test_dir))
+
+ for filename in os.listdir(bits_test_dir):
+ if os.path.isfile(os.path.join(bits_test_dir, filename)) and \
+ filename.endswith('.py2'):
+ # all test scripts are named with extension .py2 so that
+ # avocado does not try to load them. These scripts are
+ # written for python 2.7 not python 3 and hence if avocado
+ # loaded them, it would complain about python 3 specific
+ # syntaxes.
+ newfilename = os.path.splitext(filename)[0] + '.py'
+ shutil.copy2(os.path.join(bits_test_dir, filename),
+ os.path.join(target_test_dir, newfilename))
+ self.logger.info('copied test file %s to %s',
+ filename, target_test_dir)
+
+ # now remove the pyc test file if it exists, otherwise the
+ # changes in the python test script won't be executed.
+ testfile_pyc = os.path.splitext(filename)[0] + '.pyc'
+ if os.access(os.path.join(target_test_dir, testfile_pyc),
+ os.F_OK):
+ os.remove(os.path.join(target_test_dir, testfile_pyc))
+ self.logger.info('removed compiled file %s',
+ os.path.join(target_test_dir,
+ testfile_pyc))
+
+ def fix_mkrescue(self, mkrescue):
+ """ grub-mkrescue is a bash script with two variables, 'prefix' and
+ 'libdir'. They must be pointed to the right location so that the
+ iso can be generated appropriately. We point the two variables to
+ the directory where we have extracted our pre-built bits grub
+ tarball.
+ """
+ grub_x86_64_mods = os.path.join(self._workDir, 'grub-inst-x86_64-efi')
+ grub_i386_mods = os.path.join(self._workDir, 'grub-inst')
+
+ self.assertTrue(os.path.exists(grub_x86_64_mods))
+ self.assertTrue(os.path.exists(grub_i386_mods))
+
+ new_script = ""
+ with open(mkrescue, 'r', encoding='utf-8') as filehandle:
+ orig_script = filehandle.read()
+ new_script = re.sub('(^prefix=)(.*)',
+ r'\1"%s"' %grub_x86_64_mods,
+ orig_script, flags=re.M)
+ new_script = re.sub('(^libdir=)(.*)', r'\1"%s/lib"' %grub_i386_mods,
+ new_script, flags=re.M)
+
+ with open(mkrescue, 'w', encoding='utf-8') as filehandle:
+ filehandle.write(new_script)
+
+ def generate_bits_iso(self):
+ """ Uses grub-mkrescue to generate a fresh bits iso with the python
+ test scripts
+ """
+ bits_dir = os.path.join(self._workDir,
+ 'bits-%d' %self._bitsInternalVer)
+ iso_file = os.path.join(self._workDir,
+ 'bits-%d.iso' %self._bitsInternalVer)
+ mkrescue_script = os.path.join(self._workDir,
+ 'grub-inst-x86_64-efi', 'bin',
+ 'grub-mkrescue')
+
+ self.assertTrue(os.access(mkrescue_script,
+ os.R_OK | os.W_OK | os.X_OK))
+
+ self.fix_mkrescue(mkrescue_script)
+
+ self.logger.info('using grub-mkrescue for generating biosbits iso ...')
+
+ try:
+ if os.getenv('V') or os.getenv('BITS_DEBUG'):
+ subprocess.check_call([mkrescue_script, '-o', iso_file,
+ bits_dir], stderr=subprocess.STDOUT)
+ else:
+ subprocess.check_call([mkrescue_script, '-o',
+ iso_file, bits_dir],
+ stderr=subprocess.DEVNULL,
+ stdout=subprocess.DEVNULL)
+ except Exception as e: # pylint: disable=broad-except
+ self.skipTest("Error while generating the bits iso. "
+ "Pass V=1 in the environment to get more details. "
+ + str(e))
+
+ self.assertTrue(os.access(iso_file, os.R_OK))
+
+ self.logger.info('iso file %s successfully generated.', iso_file)
+
+ def setUp(self): # pylint: disable=arguments-differ
+ super().setUp('qemu-system-')
+
+ self._baseDir = os.getenv('AVOCADO_TEST_BASEDIR')
+
+ # workdir could also be avocado's own workdir in self.workdir.
+ # At present, I prefer to maintain my own temporary working
+ # directory. It gives us more control over the generated bits
+ # log files and also for debugging, we may chose not to remove
+ # this working directory so that the logs and iso can be
+ # inspected manually and archived if needed.
+ self._workDir = tempfile.mkdtemp(prefix='acpi-bits-',
+ suffix='.tmp')
+ self.logger.info('working dir: %s', self._workDir)
+
+ prebuiltDir = os.path.join(self._workDir, 'prebuilt')
+ if not os.path.isdir(prebuiltDir):
+ os.mkdir(prebuiltDir, mode=0o775)
+
+ bits_zip_file = os.path.join(prebuiltDir, 'bits-%d-%s.zip'
+ %(self._bitsInternalVer,
+ self._bitsCommitHash))
+ grub_tar_file = os.path.join(prebuiltDir,
+ 'bits-%d-%s-grub.tar.gz'
+ %(self._bitsInternalVer,
+ self._bitsCommitHash))
+
+ bitsLocalArtLoc = self.fetch_asset(self._bitsArtURL,
+ asset_hash=self._bitsArtSHA1Hash)
+ self.logger.info("downloaded bits artifacts to %s", bitsLocalArtLoc)
+
+ # extract the bits artifact in the temp working directory
+ with zipfile.ZipFile(bitsLocalArtLoc, 'r') as zref:
+ zref.extractall(prebuiltDir)
+
+ # extract the bits software in the temp working directory
+ with zipfile.ZipFile(bits_zip_file, 'r') as zref:
+ zref.extractall(self._workDir)
+
+ with tarfile.open(grub_tar_file, 'r', encoding='utf-8') as tarball:
+ tarball.extractall(self._workDir)
+
+ self.copy_test_scripts()
+ self.copy_bits_config()
+ self.generate_bits_iso()
+
+ def parse_log(self):
+ """parse the log generated by running bits tests and
+ check for failures.
+ """
+ debugconf = os.path.join(self._workDir, self._debugcon_log)
+ log = ""
+ with open(debugconf, 'r', encoding='utf-8') as filehandle:
+ log = filehandle.read()
+
+ matchiter = re.finditer(r'(.*Summary: )(\d+ passed), (\d+ failed).*',
+ log)
+ for match in matchiter:
+ # verify that no test cases failed.
+ try:
+ self.assertEqual(match.group(3).split()[0], '0',
+ 'Some bits tests seems to have failed. ' \
+ 'Please check the test logs for more info.')
+ except AssertionError as e:
+ self._print_log(log)
+ raise e
+ else:
+ if os.getenv('V') or os.getenv('BITS_DEBUG'):
+ self._print_log(log)
+
+ def tearDown(self):
+ """
+ Lets do some cleanups.
+ """
+ if self._vm:
+ self.assertFalse(not self._vm.is_running)
+ if not os.getenv('BITS_DEBUG') and self._workDir:
+ self.logger.info('removing the work directory %s', self._workDir)
+ shutil.rmtree(self._workDir)
+ else:
+ self.logger.info('not removing the work directory %s ' \
+ 'as BITS_DEBUG is ' \
+ 'passed in the environment', self._workDir)
+ super().tearDown()
+
+ def test_acpi_smbios_bits(self):
+ """The main test case implementation."""
+
+ iso_file = os.path.join(self._workDir,
+ 'bits-%d.iso' %self._bitsInternalVer)
+
+ self.assertTrue(os.access(iso_file, os.R_OK))
+
+ self._vm = QEMUBitsMachine(binary=self.qemu_bin,
+ base_temp_dir=self._workDir,
+ debugcon_log=self._debugcon_log,
+ debugcon_addr=self._debugcon_addr)
+
+ self._vm.add_args('-cdrom', '%s' %iso_file)
+ # the vm needs to be run under icount so that TCG emulation is
+ # consistent in terms of timing. smilatency tests have consistent
+ # timing requirements.
+ self._vm.add_args('-icount', 'auto')
+ # currently there is no support in bits for recognizing 64-bit SMBIOS
+ # entry points. QEMU defaults to 64-bit entry points since the
+ # upstream commit bf376f3020 ("hw/i386/pc: Default to use SMBIOS 3.0
+ # for newer machine models"). Therefore, enforce 32-bit entry point.
+ self._vm.add_args('-machine', 'smbios-entry-point-type=32')
+
+ # enable console logging
+ self._vm.set_console()
+ self._vm.launch()
+
+ self.logger.debug("Console output from bits VM follows ...")
+ c_drainer = drainer.LineLogger(self._vm.console_socket.fileno(),
+ logger=self.logger.getChild("console"),
+ stop_check=(lambda :
+ not self._vm.is_running()))
+ c_drainer.start()
+
+ # biosbits has been configured to run all the specified test suites
+ # in batch mode and then automatically initiate a vm shutdown.
+ # Set timeout to BITS_TIMEOUT for SHUTDOWN event from bits VM at par
+ # with the avocado test timeout.
+ self._vm.event_wait('SHUTDOWN', timeout=BITS_TIMEOUT)
+ self._vm.wait(timeout=None)
+ self.parse_log()
diff --git a/tests/avocado/acpi-bits/bits-config/bits-cfg.txt b/tests/avocado/acpi-bits/bits-config/bits-cfg.txt
new file mode 100644
index 0000000000..8010804453
--- /dev/null
+++ b/tests/avocado/acpi-bits/bits-config/bits-cfg.txt
@@ -0,0 +1,18 @@
+# BITS configuration file
+[bits]
+
+# To run BITS in batch mode, set batch to a list of one or more of the
+# following keywords; BITS will then run all of the requested operations, then
+# save the log file to disk.
+#
+# test: Run the full BITS testsuite.
+# acpi: Dump all ACPI structures.
+# smbios: Dump all SMBIOS structures.
+#
+# Leave batch set to an empty string to disable batch mode.
+# batch =
+
+# Uncomment the following to run all available batch operations
+# please take a look at boot/python/init.py in bits zip file
+# to see how these options are parsed and used.
+batch = test acpi smbios
diff --git a/tests/avocado/acpi-bits/bits-tests/smbios.py2 b/tests/avocado/acpi-bits/bits-tests/smbios.py2
new file mode 100644
index 0000000000..5868a7137a
--- /dev/null
+++ b/tests/avocado/acpi-bits/bits-tests/smbios.py2
@@ -0,0 +1,2434 @@
+# Copyright (c) 2015, Intel Corporation
+# All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of Intel Corporation nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# This script runs only from the biosbits VM.
+
+"""SMBIOS/DMI module."""
+
+import bits
+import bitfields
+import ctypes
+import redirect
+import struct
+import uuid
+import unpack
+import ttypager
+import sys
+
+class SMBIOS(unpack.Struct):
+ def __new__(cls):
+ if sys.platform == "BITS-EFI":
+ import efi
+ sm_ptr = efi.system_table.ConfigurationTableDict.get(efi.SMBIOS_TABLE_GUID)
+ else:
+ address = 0xF0000
+ mem = bits.memory(0xF0000, 0x10000)
+ for offset in range(0, len(mem), 16):
+ signature = (ctypes.c_char * 4).from_address(address + offset).value
+ if signature == "_SM_":
+ entry_point_length = ctypes.c_ubyte.from_address(address + offset + 5).value
+ csum = sum(map(ord, mem[offset:offset + entry_point_length])) & 0xff
+ if csum == 0:
+ sm_ptr = address + offset
+ break
+ else:
+ return None
+
+ if not sm_ptr:
+ return None
+
+ sm = super(SMBIOS, cls).__new__(cls)
+ sm._header_memory = bits.memory(sm_ptr, 0x1f)
+ return sm
+
+ def __init__(self):
+ super(SMBIOS, self).__init__()
+ u = unpack.Unpackable(self._header_memory)
+ self.add_field('header', Header(u))
+ self._structure_memory = bits.memory(self.header.structure_table_address, self.header.structure_table_length)
+ u = unpack.Unpackable(self._structure_memory)
+ self.add_field('structures', unpack.unpack_all(u, _smbios_structures, self), unpack.format_each("\n\n{!r}"))
+
+ def structure_type(self, num):
+ '''Dumps structure of given Type if present'''
+ try:
+ types_present = [self.structures[x].smbios_structure_type for x in range(len(self.structures))]
+ matrix = dict()
+ for index in range(len(types_present)):
+ if types_present.count(types_present[index]) == 1:
+ matrix[types_present[index]] = self.structures[index]
+ else: # if multiple structures of the same type, return a list of structures for the type number
+ if matrix.has_key(types_present[index]):
+ matrix[types_present[index]].append(self.structures[index])
+ else:
+ matrix[types_present[index]] = [self.structures[index]]
+ return matrix[num]
+ except:
+ print "Failure: Type {} - not found".format(num)
+
+class Header(unpack.Struct):
+ def __new__(cls, u):
+ return super(Header, cls).__new__(cls)
+
+ def __init__(self, u):
+ super(Header, self).__init__()
+ self.raw_data = u.unpack_rest()
+ u = unpack.Unpackable(self.raw_data)
+ self.add_field('anchor_string', u.unpack_one("4s"))
+ self.add_field('checksum', u.unpack_one("B"))
+ self.add_field('length', u.unpack_one("B"))
+ self.add_field('major_version', u.unpack_one("B"))
+ self.add_field('minor_version', u.unpack_one("B"))
+ self.add_field('max_structure_size', u.unpack_one("<H"))
+ self.add_field('entry_point_revision', u.unpack_one("B"))
+ self.add_field('formatted_area', u.unpack_one("5s"))
+ self.add_field('intermediate_anchor_string', u.unpack_one("5s"))
+ self.add_field('intermediate_checksum', u.unpack_one("B"))
+ self.add_field('structure_table_length', u.unpack_one("<H"))
+ self.add_field('structure_table_address', u.unpack_one("<I"))
+ self.add_field('number_structures', u.unpack_one("<H"))
+ self.add_field('bcd_revision', u.unpack_one("B"))
+ if not u.at_end():
+ self.add_field('data', u.unpack_rest())
+
+class SmbiosBaseStructure(unpack.Struct):
+ def __new__(cls, u, sm):
+ t = u.unpack_peek_one("B")
+ if cls.smbios_structure_type is not None and t != cls.smbios_structure_type:
+ return None
+ return super(SmbiosBaseStructure, cls).__new__(cls)
+
+ def __init__(self, u, sm):
+ super(SmbiosBaseStructure, self).__init__()
+ self.start_offset = u.offset
+ length = u.unpack_peek_one("<xB")
+ self.raw_data = u.unpack_raw(length)
+ self.u = unpack.Unpackable(self.raw_data)
+
+ self.strings_offset = u.offset
+ def unpack_string():
+ return "".join(iter(lambda: u.unpack_one("c"), "\x00"))
+ strings = list(iter(unpack_string, ""))
+ if not strings:
+ u.skip(1)
+
+ self.strings_length = u.offset - self.strings_offset
+ self.raw_strings = str(bits.memory(sm.header.structure_table_address + self.strings_offset, self.strings_length))
+
+ if len(strings):
+ self.strings = strings
+
+ self.add_field('type', self.u.unpack_one("B"))
+ self.add_field('length', self.u.unpack_one("B"))
+ self.add_field('handle', self.u.unpack_one("<H"))
+
+ def fini(self):
+ if not self.u.at_end():
+ self.add_field('data', self.u.unpack_rest())
+ del self.u
+
+ def fmtstr(self, i):
+ """Format the specified index and the associated string"""
+ return "{} '{}'".format(i, self.getstr(i))
+
+ def getstr(self, i):
+ """Get the string associated with the given index"""
+ if i == 0:
+ return "(none)"
+ if not hasattr(self, "strings"):
+ return "(error: structure has no strings)"
+ if i > len(self.strings):
+ return "(error: string index out of range)"
+ return self.strings[i - 1]
+
+class BIOSInformation(SmbiosBaseStructure):
+ smbios_structure_type = 0
+
+ def __init__(self, u, sm):
+ super(BIOSInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('vendor', u.unpack_one("B"), self.fmtstr)
+ self.add_field('version', u.unpack_one("B"), self.fmtstr)
+ self.add_field('starting_address_segment', u.unpack_one("<H"))
+ self.add_field('release_date', u.unpack_one("B"), self.fmtstr)
+ self.add_field('rom_size', u.unpack_one("B"))
+ self.add_field('characteristics', u.unpack_one("<Q"))
+ minor_version_str = str(sm.header.minor_version) # 34 is .34, 4 is .4, 41 is .41; compare ASCIIbetically to compare initial digits rather than numeric value
+ if (sm.header.major_version, minor_version_str) >= (2,"4"):
+ characteristic_bytes = 2
+ else:
+ characteristic_bytes = self.length - 0x12
+ self.add_field('characteristics_extensions', [u.unpack_one("B") for b in range(characteristic_bytes)])
+ if (sm.header.major_version, minor_version_str) >= (2,"4"):
+ self.add_field('major_release', u.unpack_one("B"))
+ self.add_field('minor_release', u.unpack_one("B"))
+ self.add_field('ec_major_release', u.unpack_one("B"))
+ self.add_field('ec_minor_release', u.unpack_one("B"))
+ except:
+ self.decode_failure = True
+ print "Error parsing BIOSInformation"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class SystemInformation(SmbiosBaseStructure):
+ smbios_structure_type = 1
+
+ def __init__(self, u, sm):
+ super(SystemInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('manufacturer', u.unpack_one("B"), self.fmtstr)
+ self.add_field('product_name', u.unpack_one("B"), self.fmtstr)
+ self.add_field('version', u.unpack_one("B"), self.fmtstr)
+ self.add_field('serial_number', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x8:
+ self.add_field('uuid', uuid.UUID(bytes_le=u.unpack_one("16s")))
+ wakeup_types = {
+ 0: 'Reserved',
+ 1: 'Other',
+ 2: 'Unknown',
+ 3: 'APM Timer',
+ 4: 'Modem Ring',
+ 5: 'LAN Remote',
+ 6: 'Power Switch',
+ 7: 'PCI PME#',
+ 8: 'AC Power Restored'
+ }
+ self.add_field('wakeup_type', u.unpack_one("B"), unpack.format_table("{}", wakeup_types))
+ if self.length > 0x19:
+ self.add_field('sku_number', u.unpack_one("B"), self.fmtstr)
+ self.add_field('family', u.unpack_one("B"), self.fmtstr)
+ except:
+ self.decode_failure = True
+ print "Error parsing SystemInformation"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+_board_types = {
+ 1: 'Unknown',
+ 2: 'Other',
+ 3: 'Server Blade',
+ 4: 'Connectivity Switch',
+ 5: 'System Management Module',
+ 6: 'Processor Module',
+ 7: 'I/O Module',
+ 8: 'Memory Module',
+ 9: 'Daughter Board',
+ 0xA: 'Motherboard',
+ 0xB: 'Processor/Memory Module',
+ 0xC: 'Processor/IO Module',
+ 0xD: 'Interconnect Board'
+}
+
+class BaseboardInformation(SmbiosBaseStructure):
+ smbios_structure_type = 2
+
+ def __init__(self, u, sm):
+ super(BaseboardInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('manufacturer', u.unpack_one("B"), self.fmtstr)
+ self.add_field('product', u.unpack_one("B"), self.fmtstr)
+ self.add_field('version', u.unpack_one("B"), self.fmtstr)
+ self.add_field('serial_number', u.unpack_one("B"), self.fmtstr)
+
+ if self.length > 0x8:
+ self.add_field('asset_tag', u.unpack_one("B"), self.fmtstr)
+
+ if self.length > 0x9:
+ self.add_field('feature_flags', u.unpack_one("B"))
+ self.add_field('hosting_board', bool(bitfields.getbits(self.feature_flags, 0)), "feature_flags[0]={}")
+ self.add_field('requires_daughter_card', bool(bitfields.getbits(self.feature_flags, 1)), "feature_flags[1]={}")
+ self.add_field('removable', bool(bitfields.getbits(self.feature_flags, 2)), "feature_flags[2]={}")
+ self.add_field('replaceable', bool(bitfields.getbits(self.feature_flags, 3)), "feature_flags[3]={}")
+ self.add_field('hot_swappable', bool(bitfields.getbits(self.feature_flags, 4)), "feature_flags[4]={}")
+
+ if self.length > 0xA:
+ self.add_field('location', u.unpack_one("B"), self.fmtstr)
+
+ if self.length > 0xB:
+ self.add_field('chassis_handle', u.unpack_one("<H"))
+
+ if self.length > 0xD:
+ self.add_field('board_type', u.unpack_one("B"), unpack.format_table("{}", _board_types))
+
+ if self.length > 0xE:
+ self.add_field('handle_count', u.unpack_one("B"))
+ if self.handle_count > 0:
+ self.add_field('contained_object_handles', tuple(u.unpack_one("<H") for i in range(self.handle_count)))
+ except:
+ self.decode_failure = True
+ print "Error parsing BaseboardInformation"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class SystemEnclosure(SmbiosBaseStructure):
+ smbios_structure_type = 3
+
+ def __init__(self, u, sm):
+ super(SystemEnclosure, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('manufacturer', u.unpack_one("B"), self.fmtstr)
+ self.add_field('enumerated_type', u.unpack_one("B"))
+ self.add_field('chassis_lock_present', bool(bitfields.getbits(self.enumerated_type, 7)), "enumerated_type[7]={}")
+ board_types = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Desktop',
+ 0x04: 'Low Profile Desktop',
+ 0x05: 'Pizza Box',
+ 0x06: 'Mini Tower',
+ 0x07: 'Tower',
+ 0x08: 'Portable',
+ 0x09: 'Laptop',
+ 0x0A: 'Notebook',
+ 0x0B: 'Hand Held',
+ 0x0C: 'Docking Station',
+ 0x0D: 'All in One',
+ 0x0E: 'Sub Notebook',
+ 0x0F: 'Space-saving',
+ 0x10: 'Lunch Box',
+ 0x11: 'Main Server Chassis',
+ 0x12: 'Expansion Chassis',
+ 0x13: 'SubChassis',
+ 0x14: 'Bus Expansion Chassis',
+ 0x15: 'Peripheral Chassis',
+ 0x16: 'RAID Chassis',
+ 0x17: 'Rack Mount Chassis',
+ 0x18: 'Sealed-case PC',
+ 0x19: 'Multi-system chassis W',
+ 0x1A: 'Compact PCI',
+ 0x1B: 'Advanced TCA',
+ 0x1C: 'Blade',
+ 0x1D: 'Blade Enclosure',
+ }
+ self.add_field('system_enclosure_type', bitfields.getbits(self.enumerated_type, 6, 0), unpack.format_table("enumerated_type[6:0]={}", board_types))
+ self.add_field('version', u.unpack_one("B"), self.fmtstr)
+ self.add_field('serial_number', u.unpack_one("B"), self.fmtstr)
+ self.add_field('asset_tag', u.unpack_one("B"), self.fmtstr)
+ minor_version_str = str(sm.header.minor_version) # 34 is .34, 4 is .4, 41 is .41; compare ASCIIbetically to compare initial digits rather than numeric value
+ if self.length > 9:
+ chassis_states = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Safe',
+ 0x04: 'Warning',
+ 0x05: 'Critical',
+ 0x06: 'Non-recoverable',
+ }
+ self.add_field('bootup_state', u.unpack_one("B"), unpack.format_table("{}", chassis_states))
+ self.add_field('power_supply_state', u.unpack_one("B"), unpack.format_table("{}", chassis_states))
+ self.add_field('thermal_state', u.unpack_one("B"), unpack.format_table("{}", chassis_states))
+ security_states = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'None',
+ 0x04: 'External interface locked out',
+ 0x05: 'External interface enabled',
+ }
+ self.add_field('security_status', u.unpack_one("B"), unpack.format_table("{}", security_states))
+ if self.length > 0xd:
+ self.add_field('oem_defined', u.unpack_one("<I"))
+ if self.length > 0x11:
+ self.add_field('height', u.unpack_one("B"))
+ self.add_field('num_power_cords', u.unpack_one("B"))
+ self.add_field('contained_element_count', u.unpack_one("B"))
+ self.add_field('contained_element_length', u.unpack_one("B"))
+ if getattr(self, 'contained_element_count', 0):
+ self.add_field('contained_elements', tuple(SystemEnclosureContainedElement(u, self.contained_element_length) for i in range(self.contained_element_count)))
+ if self.length > (0x15 + (getattr(self, 'contained_element_count', 0) * getattr(self, 'contained_element_length', 0))):
+ self.add_field('sku_number', u.unpack_one("B"), self.fmtstr)
+ except:
+ self.decode_failure = True
+ print "Error parsing SystemEnclosure"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class SystemEnclosureContainedElement(unpack.Struct):
+ def __init__(self, u, length):
+ super(SystemEnclosureContainedElement, self).__init__()
+ self.start_offset = u.offset
+ self.raw_data = u.unpack_raw(length)
+ self.u = unpack.Unpackable(self.raw_data)
+ u = self.u
+ self.add_field('contained_element_type', u.unpack_one("B"))
+ type_selections = {
+ 0: 'SMBIOS baseboard type enumeration',
+ 1: 'SMBIOS structure type enumeration',
+ }
+ self.add_field('type_select', bitfields.getbits(self.contained_element_type, 7), unpack.format_table("contained_element_type[7]={}", type_selections))
+ self.add_field('type', bitfields.getbits(self.contained_element_type, 6, 0))
+ if self.type_select == 0:
+ self.add_field('smbios_board_type', self.type, unpack.format_table("{}", _board_types))
+ else:
+ self.add_field('smbios_structure_type', self.type)
+ self.add_field('minimum', u.unpack_one("B"))
+ self.add_field('maximum', u.unpack_one("B"))
+ if not u.at_end():
+ self.add_field('data', u.unpack_rest())
+ del self.u
+
+class ProcessorInformation(SmbiosBaseStructure):
+ smbios_structure_type = 4
+
+ def __init__(self, u, sm):
+ super(ProcessorInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('socket_designation', u.unpack_one("B"), self.fmtstr)
+ processor_types = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Central Processor',
+ 0x04: 'Math Processor',
+ 0x05: 'DSP Processor',
+ 0x06: 'Video Processor',
+ }
+ self.add_field('processor_type', u.unpack_one("B"), unpack.format_table("{}", processor_types))
+ self.add_field('processor_family', u.unpack_one("B"))
+ self.add_field('processor_manufacturer', u.unpack_one("B"), self.fmtstr)
+ self.add_field('processor_id', u.unpack_one("<Q"))
+ self.add_field('processor_version', u.unpack_one("B"), self.fmtstr)
+ self.add_field('voltage', u.unpack_one("B"))
+ self.add_field('external_clock', u.unpack_one("<H"))
+ self.add_field('max_speed', u.unpack_one("<H"))
+ self.add_field('current_speed', u.unpack_one("<H"))
+ self.add_field('status', u.unpack_one("B"))
+ processor_upgrades = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Daughter Board',
+ 0x04: 'ZIF Socket',
+ 0x05: 'Replaceable Piggy Back',
+ 0x06: 'None',
+ 0x07: 'LIF Socket',
+ 0x08: 'Slot 1',
+ 0x09: 'Slot 2',
+ 0x0A: '370-pin socket',
+ 0x0B: 'Slot A',
+ 0x0C: 'Slot M',
+ 0x0D: 'Socket 423',
+ 0x0E: 'Socket A (Socket 462)',
+ 0x0F: 'Socket 478',
+ 0x10: 'Socket 754',
+ 0x11: 'Socket 940',
+ 0x12: 'Socket 939',
+ 0x13: 'Socket mPGA604',
+ 0x14: 'Socket LGA771',
+ 0x15: 'Socket LGA775',
+ 0x16: 'Socket S1',
+ 0x17: 'Socket AM2',
+ 0x18: 'Socket F (1207)',
+ 0x19: 'Socket LGA1366',
+ 0x1A: 'Socket G34',
+ 0x1B: 'Socket AM3',
+ 0x1C: 'Socket C32',
+ 0x1D: 'Socket LGA1156',
+ 0x1E: 'Socket LGA1567',
+ 0x1F: 'Socket PGA988A',
+ 0x20: 'Socket BGA1288',
+ 0x21: 'Socket rPGA988B',
+ 0x22: 'Socket BGA1023',
+ 0x23: 'Socket BGA1224',
+ 0x24: 'Socket BGA1155',
+ 0x25: 'Socket LGA1356',
+ 0x26: 'Socket LGA2011',
+ 0x27: 'Socket FS1',
+ 0x28: 'Socket FS2',
+ 0x29: 'Socket FM1',
+ 0x2A: 'Socket FM2',
+ }
+ self.add_field('processor_upgrade', u.unpack_one("B"), unpack.format_table("{}", processor_upgrades))
+ if self.length > 0x1A:
+ self.add_field('l1_cache_handle', u.unpack_one("<H"))
+ self.add_field('l2_cache_handle', u.unpack_one("<H"))
+ self.add_field('l3_cache_handle', u.unpack_one("<H"))
+ if self.length > 0x20:
+ self.add_field('serial_number', u.unpack_one("B"), self.fmtstr)
+ self.add_field('asset_tag', u.unpack_one("B"), self.fmtstr)
+ self.add_field('part_number', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x24:
+ self.add_field('core_count', u.unpack_one("B"))
+ self.add_field('core_enabled', u.unpack_one("B"))
+ self.add_field('thread_count', u.unpack_one("B"))
+ self.add_field('processor_characteristics', u.unpack_one("<H"))
+ if self.length > 0x28:
+ self.add_field('processor_family_2', u.unpack_one("<H"))
+ if self.length > 0x2A:
+ self.add_field('core_count2', u.unpack_one("<H"))
+ self.add_field('core_enabled2', u.unpack_one("<H"))
+ self.add_field('thread_count2', u.unpack_one("<H"))
+ except:
+ self.decode_failure = True
+ print "Error parsing Processor Information"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class MemoryControllerInformation(SmbiosBaseStructure): #obsolete starting with v2.1
+ smbios_structure_type = 5
+
+ def __init__(self, u, sm):
+ super(MemoryControllerInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ _error_detecting_method = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'None',
+ 0x04: '8-bit Parity',
+ 0x05: '32-bit ECC',
+ 0x06: '64-bit ECC',
+ 0x07: '128-bit ECC',
+ 0x08: 'CRC'
+ }
+ self.add_field('error_detecting_method', u.unpack_one("B"), unpack.format_table("{}", _error_detecting_method))
+ self.add_field('error_correcting_capability', u.unpack_one("B"))
+ _interleaves = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'One-Way Interleave',
+ 0x04: 'Two-Way Interleave',
+ 0x05: 'Four-Way Interleave',
+ 0x06: 'Eight-Way Interleave',
+ 0x07: 'Sixteen-Way Interleave'
+ }
+ self.add_field('supported_interleave', u.unpack_one("B"), unpack.format_table("{}", _interleaves))
+ self.add_field('current_interleave', u.unpack_one("B"), unpack.format_table("{}", _interleaves))
+ self.add_field('max_memory_module_size', u.unpack_one("B"), self.fmtstr)
+ self.add_field('supported_speeds', u.unpack_one("<H"))
+ self.add_field('supported_memory_types', u.unpack_one("<H"))
+ self.add_field('memory_module_voltage', u.unpack_one("B"))
+ self.add_field('req_voltage_b2', bitfields.getbits(self.memory_module_voltage, 2), "memory_module_voltage[2]={}")
+ self.add_field('req_voltage_b1', bitfields.getbits(self.memory_module_voltage, 1), "memory_module_voltage[1]={}")
+ self.add_field('req_voltage_b0', bitfields.getbits(self.memory_module_voltage, 0), "memory_module_voltage[0]={}")
+ self.add_field('num_associated_memory_slots', u.unpack_one("B"))
+ self.add_field('memory_module_configuration_handles', u.unpack_one("<(self.num_associated_memory_slots)H"))
+ self.add_field('enabled_error_correcting_capabilities', u.unpack_one("B"))
+ except:
+ self.decode_failure = True
+ print "Error parsing MemoryControllerInformation"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class MemoryModuleInformation(SmbiosBaseStructure): #obsolete starting with v2.1
+ smbios_structure_type = 6
+
+ def __init__(self, u, sm):
+ super(MemoryModuleInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('socket_designation', u.unpack_one("B"), self.fmtstr)
+ self.add_field('bank_connections', u.unpack_one("B"))
+ self.add_field('current_speed', u.unpack_one("B"))
+ self.add_field('current_memory_type', u.unpack_one("<H"))
+ _mem_connection = {
+ 0: 'single',
+ 1: 'double-bank'
+ }
+ self.add_field('installed_mem', u.unpack_one("B"))
+ self.add_field('installed_size', bitfields.getbits(self.installed_mem, 6, 0), "installed_mem[6:0]={}")
+ self.add_field('installed_memory_module_connection', bitfields.getbits(self.installed_mem, 7), unpack.format_table("installed_mem[7]={}", _mem_connection))
+ self.add_field('enabled_mem', u.unpack_one("B"))
+ self.add_field('enabled_size', bitfields.getbits(self.installed_mem, 6, 0), "enabled_mem[6:0]={}")
+ self.add_field('enabled_memory_module_connection', bitfields.getbits(self.installed_mem, 7), unpack.format_table("enabled_mem[7]={}", _mem_connection))
+ self.add_field('error_status', u.unpack_one("B"))
+ self.add_field('error_status_info_obstained_from_event_log', bool(bitfields.getbits(self.error_status, 2)), unpack.format_table("error_status[2]={}", _mem_connection))
+ self.add_field('correctable_errors_received', bool(bitfields.getbits(self.error_status, 1)), unpack.format_table("error_status[1]={}", _mem_connection))
+ self.add_field('uncorrectable_errors_received', bool(bitfields.getbits(self.error_status, 0)), unpack.format_table("error_status[0]={}", _mem_connection))
+ except:
+ self.decode_failure = True
+ print "Error parsing MemoryModuleInformation"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class CacheInformation(SmbiosBaseStructure):
+ smbios_structure_type = 7
+
+ def __init__(self, u, sm):
+ super(CacheInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('socket_designation', u.unpack_one("B"), self.fmtstr)
+ processor_types = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Central Processor',
+ 0x04: 'Math Processor',
+ 0x05: 'DSP Processor',
+ 0x06: 'Video Processor',
+ }
+ self.add_field('cache_configuration', u.unpack_one("<H"))
+ _operational_mode = {
+ 0b00: 'Write Through',
+ 0b01: 'Write Back',
+ 0b10: 'Varies with Memory Address',
+ 0b11: 'Unknown'
+ }
+ self.add_field('operational_mode', bitfields.getbits(self.cache_configuration, 9, 8), unpack.format_table("cache_configuration[9:8]={}", _operational_mode))
+ self.add_field('enabled_at_boot_time', bool(bitfields.getbits(self.cache_configuration, 7)), "cache_configuration[7]={}")
+ _location = {
+ 0b00: 'Internal',
+ 0b01: 'External',
+ 0b10: 'Reserved',
+ 0b11: 'Unknown'
+ }
+ self.add_field('location_relative_to_cpu_module', bitfields.getbits(self.cache_configuration, 6, 5), unpack.format_table("cache_configuration[6:5]={}", _location))
+ self.add_field('cache_socketed', bool(bitfields.getbits(self.cache_configuration, 3)), "cache_configuration[3]={}")
+ self.add_field('cache_level', bitfields.getbits(self.cache_configuration, 2, 0), "cache_configuration[2:0]={}")
+ self.add_field('max_cache_size', u.unpack_one("<H"))
+ _granularity = {
+ 0: '1K granularity',
+ 1: '64K granularity'
+ }
+ self.add_field('max_granularity', bitfields.getbits(self.cache_configuration, 15), unpack.format_table("max_cache_size[15]={}", _granularity))
+ self.add_field('max_size_in_granularity', bitfields.getbits(self.cache_configuration, 14, 0), "max_cache_size[14, 0]={}")
+ self.add_field('installed_size', u.unpack_one("<H"))
+ if self.installed_size != 0:
+ self.add_field('installed_granularity', bitfields.getbits(self.cache_configuration, 15), unpack.format_table("installed_size[15]={}", _granularity))
+ self.add_field('installed_size_in_granularity', bitfields.getbits(self.cache_configuration, 14, 0), "installed_size[14, 0]={}")
+ self.add_field('supported_sram_type', u.unpack_one("<H"))
+ self.add_field('current_sram_type', u.unpack_one("<H"))
+ if self.length > 0x0F:
+ self.add_field('cache_speed', u.unpack_one("B"))
+ if self.length > 0x10:
+ _error_correction = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'None',
+ 0x04: 'Parity',
+ 0x05: 'Single-bit ECC',
+ 0x06: 'Multi-bit ECC'
+ }
+ self.add_field('error_correction', u.unpack_one("B"), unpack.format_table("{}", _error_correction))
+ if self.length > 0x10:
+ _system_cache_type = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Instruction',
+ 0x04: 'Data',
+ 0x05: 'Unified'
+ }
+ self.add_field('system_cache_type', u.unpack_one("B"), unpack.format_table("{}", _system_cache_type))
+ if self.length > 0x12:
+ _associativity = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Direct Mapped',
+ 0x04: '2-way Set-Associative',
+ 0x05: '4-way Set-Associative',
+ 0x06: 'Fully Associative',
+ 0x07: '8-way Set-Associative',
+ 0x08: '16-way Set-Associative',
+ 0x09: '12-way Set-Associative',
+ 0x0A: '24-way Set-Associative',
+ 0x0B: '32-way Set-Associative',
+ 0x0C: '48-way Set-Associative',
+ 0x0D: '64-way Set-Associative',
+ 0x0E: '20-way Set-Associative'
+ }
+ self.add_field('associativity', u.unpack_one("B"), unpack.format_table("{}", _associativity))
+
+ except:
+ self.decode_failure = True
+ print "Error parsing CacheInformation"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class PortConnectorInfo(SmbiosBaseStructure):
+ smbios_structure_type = 8
+
+ def __init__(self, u, sm):
+ super(PortConnectorInfo, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('internal_reference_designator', u.unpack_one("B"), self.fmtstr)
+ connector_types = {
+ 0x00: 'None',
+ 0x01: 'Centronics',
+ 0x02: 'Mini Centronics',
+ 0x03: 'Proprietary',
+ 0x04: 'DB-25 pin male',
+ 0x05: 'DB-25 pin female',
+ 0x06: 'DB-15 pin male',
+ 0x07: 'DB-15 pin female',
+ 0x08: 'DB-9 pin male',
+ 0x09: 'DB-9 pin female',
+ 0x0A: 'RJ-11',
+ 0x0B: 'RJ-45',
+ 0x0C: '50-pin MiniSCSI',
+ 0x0D: 'Mini-DIN',
+ 0x0E: 'Micro-DIN',
+ 0x0F: 'PS/2',
+ 0x10: 'Infrared',
+ 0x11: 'HP-HIL',
+ 0x12: 'Access Bus (USB)',
+ 0x13: 'SSA SCSI',
+ 0x14: 'Circular DIN-8 male',
+ 0x15: 'Circular DIN-8 female',
+ 0x16: 'On Board IDE',
+ 0x17: 'On Board Floppy',
+ 0x18: '9-pin Dual Inline (pin 10 cut)',
+ 0x19: '25-pin Dual Inline (pin 26 cut)',
+ 0x1A: '50-pin Dual Inline',
+ 0x1B: '68-pin Dual Inline',
+ 0x1C: 'On Board Sound Input from CD-ROM',
+ 0x1D: 'Mini-Centronics Type-14',
+ 0x1E: 'Mini-Centronics Type-26',
+ 0x1F: 'Mini-jack (headphones)',
+ 0x20: 'BNC',
+ 0x21: '1394',
+ 0x22: 'SAS/SATA Plug Receptacle',
+ 0xA0: 'PC-98',
+ 0xA1: 'PC-98Hireso',
+ 0xA2: 'PC-H98',
+ 0xA3: 'PC-98Note',
+ 0xA4: 'PC-98Full',
+ 0xFF: 'Other',
+ }
+ self.add_field('internal_connector_type', u.unpack_one("B"), unpack.format_table("{}", connector_types))
+ self.add_field('external_reference_designator', u.unpack_one("B"), self.fmtstr)
+ self.add_field('external_connector_type', u.unpack_one("B"), unpack.format_table("{}", connector_types))
+ port_types = {
+ 0x00: 'None',
+ 0x01: 'Parallel Port XT/AT Compatible',
+ 0x02: 'Parallel Port PS/2',
+ 0x03: 'Parallel Port ECP',
+ 0x04: 'Parallel Port EPP',
+ 0x05: 'Parallel Port ECP/EPP',
+ 0x06: 'Serial Port XT/AT Compatible',
+ 0x07: 'Serial Port 16450 Compatible',
+ 0x08: 'Serial Port 16550 Compatible',
+ 0x09: 'Serial Port 16550A Compatible',
+ 0x0A: 'SCSI Port',
+ 0x0B: 'MIDI Port',
+ 0x0C: 'Joy Stick Port',
+ 0x0D: 'Keyboard Port',
+ 0x0E: 'Mouse Port',
+ 0x0F: 'SSA SCSI',
+ 0x10: 'USB',
+ 0x11: 'FireWire (IEEE P1394)',
+ 0x12: 'PCMCIA Type I2',
+ 0x13: 'PCMCIA Type II',
+ 0x14: 'PCMCIA Type III',
+ 0x15: 'Cardbus',
+ 0x16: 'Access Bus Port',
+ 0x17: 'SCSI II',
+ 0x18: 'SCSI Wide',
+ 0x19: 'PC-98',
+ 0x1A: 'PC-98-Hireso',
+ 0x1B: 'PC-H98',
+ 0x1C: 'Video Port',
+ 0x1D: 'Audio Port',
+ 0x1E: 'Modem Port',
+ 0x1F: 'Network Port',
+ 0x20: 'SATA',
+ 0x21: 'SAS',
+ 0xA0: '8251 Compatible',
+ 0xA1: '8251 FIFO Compatible',
+ 0xFF: 'Other',
+ }
+ self.add_field('port_type', u.unpack_one("B"), unpack.format_table("{}", port_types))
+ except:
+ self.decodeFailure = True
+ print "Error parsing PortConnectorInfo"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class SystemSlots(SmbiosBaseStructure):
+ smbios_structure_type = 9
+
+ def __init__(self, u, sm):
+ super(SystemSlots, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('designation', u.unpack_one("B"), self.fmtstr)
+ _slot_types = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'ISA',
+ 0x04: 'MCA',
+ 0x05: 'EISA',
+ 0x06: 'PCI',
+ 0x07: 'PC Card (PCMCIA)',
+ 0x08: 'VL-VESA',
+ 0x09: 'Proprietary',
+ 0x0A: 'Processor Card Slot',
+ 0x0B: 'Proprietary Memory Card Slot',
+ 0x0C: 'I/O Riser Card Slot',
+ 0x0D: 'NuBus',
+ 0x0E: 'PCI 66MHz Capable',
+ 0x0F: 'AGP',
+ 0x10: 'AGP 2X',
+ 0x11: 'AGP 4X',
+ 0x12: 'PCI-X',
+ 0x13: 'AGP 8X',
+ 0xA0: 'PC-98/C20',
+ 0xA1: 'PC-98/C24',
+ 0xA2: 'PC-98/E',
+ 0xA3: 'PC-98/Local Bus',
+ 0xA4: 'PC-98/Card',
+ 0xA5: 'PCI Express',
+ 0xA6: 'PCI Express x1',
+ 0xA7: 'PCI Express x2',
+ 0xA8: 'PCI Express x4',
+ 0xA9: 'PCI Express x8',
+ 0xAA: 'PCI Express x16',
+ 0xAB: 'PCI Express Gen 2',
+ 0xAC: 'PCI Express Gen 2 x1',
+ 0xAD: 'PCI Express Gen 2 x2',
+ 0xAE: 'PCI Express Gen 2 x4',
+ 0xAF: 'PCI Express Gen 2 x8',
+ 0xB0: 'PCI Express Gen 2 x16',
+ 0xB1: 'PCI Express Gen 3',
+ 0xB2: 'PCI Express Gen 3 x1',
+ 0xB3: 'PCI Express Gen 3 x2',
+ 0xB4: 'PCI Express Gen 3 x4',
+ 0xB5: 'PCI Express Gen 3 x8',
+ 0xB6: 'PCI Express Gen 3 x16',
+ }
+ self.add_field('slot_type', u.unpack_one("B"), unpack.format_table("{}", _slot_types))
+ _slot_data_bus_widths = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: '8 bit',
+ 0x04: '16 bit',
+ 0x05: '32 bit',
+ 0x06: '64 bit',
+ 0x07: '128 bit',
+ 0x08: '1x or x1',
+ 0x09: '2x or x2',
+ 0x0A: '4x or x4',
+ 0x0B: '8x or x8',
+ 0x0C: '12x or x12',
+ 0x0D: '16x or x16',
+ 0x0E: '32x or x32',
+ }
+ self.add_field('slot_data_bus_width', u.unpack_one('B'), unpack.format_table("{}", _slot_data_bus_widths))
+ _current_usages = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Available',
+ 0x04: 'In use',
+ }
+ self.add_field('current_usage', u.unpack_one('B'), unpack.format_table("{}", _current_usages))
+ _slot_lengths = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Short Length',
+ 0x04: 'Long Length',
+ }
+ self.add_field('slot_length', u.unpack_one('B'), unpack.format_table("{}", _slot_lengths))
+ self.add_field('slot_id', u.unpack_one('<H'))
+ self.add_field('characteristics1', u.unpack_one('B'))
+ self.add_field('characteristics_unknown', bool(bitfields.getbits(self.characteristics1, 0)), "characteristics1[0]={}")
+ self.add_field('provides_5_0_volts', bool(bitfields.getbits(self.characteristics1, 1)), "characteristics1[1]={}")
+ self.add_field('provides_3_3_volts', bool(bitfields.getbits(self.characteristics1, 2)), "characteristics1[2]={}")
+ self.add_field('shared_slot', bool(bitfields.getbits(self.characteristics1, 3)), "characteristics1[3]={}")
+ self.add_field('supports_pc_card_16', bool(bitfields.getbits(self.characteristics1, 4)), "characteristics1[4]={}")
+ self.add_field('supports_cardbus', bool(bitfields.getbits(self.characteristics1, 5)), "characteristics1[5]={}")
+ self.add_field('supports_zoom_video', bool(bitfields.getbits(self.characteristics1, 6)), "characteristics1[6]={}")
+ self.add_field('supports_modem_ring_resume', bool(bitfields.getbits(self.characteristics1, 7)), "characteristics1[7]={}")
+ if self.length > 0x0C:
+ self.add_field('characteristics2', u.unpack_one('B'))
+ self.add_field('supports_PME', bool(bitfields.getbits(self.characteristics2, 0)), "characteristics2[0]={}")
+ self.add_field('supports_hot_plug', bool(bitfields.getbits(self.characteristics2, 1)), "characteristics2[1]={}")
+ self.add_field('supports_smbus', bool(bitfields.getbits(self.characteristics2, 2)), "characteristics2[2]={}")
+ if self.length > 0x0D:
+ self.add_field('segment_group_number', u.unpack_one('<H'))
+ self.add_field('bus_number', u.unpack_one('B'))
+ self.add_field('device_function_number', u.unpack_one('B'))
+ self.add_field('device_number', bitfields.getbits(self.device_function_number, 7, 3), "device_function_number[7:3]={}")
+ self.add_field('function_number', bitfields.getbits(self.device_function_number, 2, 0), "device_function_number[2:0]={}")
+ except:
+ self.decodeFailure = True
+ print "Error parsing SystemSlots"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class OnBoardDevicesInformation(SmbiosBaseStructure):
+ smbios_structure_type = 10
+
+ def __init__(self, u, sm):
+ super(OnBoardDevicesInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('device_type', u.unpack_one("B"))
+ self.add_field('device_enabled', bool(bitfields.getbits(self.device_type, 7)), "device_type[7]={}")
+ _device_types = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Video',
+ 0x04: 'SCSI Controller',
+ 0x05: 'Ethernet',
+ 0x06: 'Token Ring',
+ 0x07: 'Sound',
+ 0x08: 'PATA Controller',
+ 0x09: 'SATA Controller',
+ 0x0A: 'SAS Controller'
+ }
+ self.add_field('type_of_device', bitfields.getbits(self.device_type, 6, 0), unpack.format_table("device_type[6:0]={}", _device_types))
+ self.add_field('description_string', u.unpack_one("B"), self.fmtstr)
+ except:
+ self.decodeFailure = True
+ print "Error parsing OnBoardDevicesInformation"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class OEMStrings(SmbiosBaseStructure):
+ smbios_structure_type = 11
+
+ def __init__(self, u, sm):
+ super(OEMStrings, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('count', u.unpack_one("B"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing OEMStrings"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class SystemConfigOptions(SmbiosBaseStructure):
+ smbios_structure_type = 12
+
+ def __init__(self, u, sm):
+ super(SystemConfigOptions, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('count', u.unpack_one("B"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing SystemConfigOptions"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class BIOSLanguageInformation(SmbiosBaseStructure):
+ smbios_structure_type = 13
+
+ def __init__(self, u, sm):
+ super(BIOSLanguageInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('installable_languages', u.unpack_one("B"))
+ if self.length > 0x05:
+ self.add_field('flags', u.unpack_one('B'))
+ self.add_field('abbreviated_format', bool(bitfields.getbits(self.flags, 0)), "flags[0]={}")
+ if self.length > 0x6:
+ u.skip(15)
+ self.add_field('current_language', u.unpack_one('B'), self.fmtstr)
+ except:
+ self.decodeFailure = True
+ print "Error parsing BIOSLanguageInformation"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class GroupAssociations(SmbiosBaseStructure):
+ smbios_structure_type = 14
+
+ def __init__(self, u, sm):
+ super(GroupAssociations, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('group_name', u.unpack_one("B"), self.fmtstr)
+ self.add_field('item_type', u.unpack_one('B'))
+ self.add_field('item_handle', u.unpack_one('<H'))
+ except:
+ self.decodeFailure = True
+ print "Error parsing GroupAssociations"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class SystemEventLog(SmbiosBaseStructure):
+ smbios_structure_type = 15
+
+ def __init__(self, u, sm):
+ super(SystemEventLog, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('log_area_length', u.unpack_one("<H"))
+ self.add_field('log_header_start_offset', u.unpack_one('<H'))
+ self.add_field('log_data_start_offset', u.unpack_one('<H'))
+ _access_method = {
+ 0x00: 'Indexed I/O: 1 8-bit index port, 1 8-bit data port',
+ 0x01: 'Indexed I/O: 2 8-bit index ports, 1 8-bit data port',
+ 0x02: 'Indexed I/O: 1 16-bit index port, 1 8-bit data port',
+ 0x03: 'Memory-mapped physical 32-bit address',
+ 0x04: 'Available through General-Purpose NonVolatile Data functions',
+ xrange(0x05, 0x07F): 'Available for future assignment',
+ xrange(0x80, 0xFF): 'BIOS Vendor/OEM-specific'
+ }
+ self.add_field('access_method', u.unpack_one('B'), unpack.format_table("{}", _access_method))
+ self.add_field('log_status', u.unpack_one('B'))
+ self.add_field('log_area_full', bool(bitfields.getbits(self.log_status, 1)), "log_status[1]={}")
+ self.add_field('log_area_valid', bool(bitfields.getbits(self.log_status, 0)), "log_status[0]={}")
+ self.add_field('log_change_token', u.unpack_one('<I'))
+ self.add_field('access_method_address', u.unpack_one('<I'))
+ if self.length > 0x14:
+ _log_header_formats = {
+ 0: 'No header',
+ 1: 'Type 1 log header',
+ xrange(2, 0x7f): 'Available for future assignment',
+ xrange(0x80, 0xff): 'BIOS vendor or OEM-specific format'
+ }
+ self.add_field('log_header_format', u.unpack_one("B"), unpack.format_table("{}", _log_header_formats))
+ if self.length > 0x15:
+ self.add_field('num_supported_log_type_descriptors', u.unpack_one('B'))
+ if self.length > 0x16:
+ self.add_field('length_log_type_descriptor', u.unpack_one('B'))
+ if self.length != (0x17 + (self.num_supported_log_type_descriptors * self.length_log_type_descriptor)):
+ print "Error: structure length ({}) != 0x17 + (num_supported_log_type_descriptors ({}) * length_log_type_descriptor({}))".format(self.length, self.num_supported_log_type_descriptors, self.length_log_type_descriptor)
+ print "structure length = {}".format(self.length)
+ print "num_supported_log_type_descriptors = {}".format(self.num_supported_log_type_descriptors)
+ print "length_log_type_descriptor = {}".format(self.length_log_type_descriptor)
+ self.decodeFailure = True
+ self.add_field('descriptors', tuple(EventLogDescriptor.unpack(u) for i in range(self.num_supported_log_type_descriptors)), unpack.format_each("\n{!r}"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing SystemEventLog"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class EventLogDescriptor(unpack.Struct):
+ @staticmethod
+ def _unpack(u):
+ _event_log_type_descriptors = {
+ 0x00: 'Reserved',
+ 0x01: 'Single-bit ECC memory error',
+ 0x02: 'Multi-bit ECC memory error',
+ 0x03: 'Parity memory error',
+ 0x04: 'Bus time-out',
+ 0x05: 'I/O Channel Check',
+ 0x06: 'Software NMI',
+ 0x07: 'POST Memory Resize',
+ 0x08: 'POST Error',
+ 0x09: 'PCI Parity Error',
+ 0x0A: 'PCI System Error',
+ 0x0B: 'CPU Failure',
+ 0x0C: 'EISA FailSafe Timer time-out',
+ 0x0D: 'Correctable memory log disabled',
+ 0x0E: 'Logging disabled for a specific Event Type - too many errors of the same type received in a short amount of time',
+ 0x0F: 'Reserved',
+ 0x10: 'System Limit Exceeded',
+ 0x11: 'Asynchronous hardware timer expired and issued a system reset',
+ 0x12: 'System configuration information',
+ 0x13: 'Hard-disk information',
+ 0x14: 'System reconfigured',
+ 0x15: 'Uncorrectable CPU-complex error',
+ 0x16: 'Log Area Reset/Cleared',
+ 0x17: 'System boot',
+ xrange(0x18, 0x7F): 'Unused, available for assignment',
+ xrange(0x80, 0xFE): 'Available for system- and OEM-specific assignments',
+ 0xFF: 'End of log'
+ }
+ yield 'log_type', u.unpack_one('B'), unpack.format_table("{}", _event_log_type_descriptors)
+ _event_log_format = {
+ 0x00: 'None',
+ 0x01: 'Handle',
+ 0x02: 'Multiple-Event',
+ 0x03: 'Multiple-Event Handle',
+ 0x04: 'POST Results Bitmap',
+ 0x05: 'System Management Type',
+ 0x06: 'Multiple-Event System Management Type',
+ xrange(0x80, 0xFF): 'OEM assigned'
+ }
+ yield 'variable_data_format_type', u.unpack_one('B'), unpack.format_table("{}", _event_log_format)
+
+class PhysicalMemoryArray(SmbiosBaseStructure):
+ smbios_structure_type = 16
+
+ def __init__(self, u, sm):
+ super(PhysicalMemoryArray, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ _location_field = {
+ 0x01: "Other",
+ 0x02: "Unknown",
+ 0x03: "System board or motherboard",
+ 0x04: "ISA add-on card",
+ 0x05: "EISA add-on card",
+ 0x06: "PCI add-on card",
+ 0x07: "MCA add-on card",
+ 0x08: "PCMCIA add-on card",
+ 0x09: "Proprietary add-on card",
+ 0x0A: "NuBus",
+ 0xA0: "PC-98/C20 add-on card",
+ 0xA1: "PC-98/C24 add-on card",
+ 0xA2: "PC-98/E add-on card",
+ 0xA3: "PC-98/Local bus add-on card"
+ }
+ self.add_field('location', u.unpack_one("B"), unpack.format_table("{}", _location_field))
+ if self.length > 0x05:
+ _use = {
+ 0x01: "Other",
+ 0x02: "Unknown",
+ 0x03: "System memory",
+ 0x04: "Video memory",
+ 0x05: "Flash memory",
+ 0x06: "Non-volatile RAM",
+ 0x07: "Cache memory"
+ }
+ self.add_field('use', u.unpack_one('B'), unpack.format_table("{}", _use))
+ if self.length > 0x06:
+ _error_correction = {
+ 0x01: "Other",
+ 0x02: "Unknown",
+ 0x03: "None",
+ 0x04: "Parity",
+ 0x05: "Single-bit ECC",
+ 0x06: "Multi-bit ECC",
+ 0x07: "CRC"
+ }
+ self.add_field('memory_error_correction', u.unpack_one('B'), unpack.format_table("{}", _error_correction))
+ if self.length > 0x07:
+ self.add_field('maximum_capacity', u.unpack_one('<I'))
+ if self.length > 0x0B:
+ self.add_field('memory_error_information_handle', u.unpack_one('<H'))
+ if self.length > 0x0D:
+ self.add_field('num_memory_devices', u.unpack_one('<H'))
+ if self.length > 0x0F:
+ self.add_field('extended_maximum_capacity', u.unpack_one('<Q'))
+ except:
+ self.decodeFailure = True
+ print "Error parsing PhysicalMemoryArray"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class MemoryDevice(SmbiosBaseStructure):
+ smbios_structure_type = 17
+
+ def __init__(self, u, sm):
+ super(MemoryDevice, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('physical_memory_array_handle', u.unpack_one("<H"))
+ if self.length > 0x6:
+ self.add_field('memory_error_information_handle', u.unpack_one("<H"))
+ if self.length > 0x8:
+ self.add_field('total_width', u.unpack_one("<H"))
+ if self.length > 0xA:
+ self.add_field('data_width', u.unpack_one("<H"))
+ if self.length > 0xC:
+ self.add_field('size', u.unpack_one("<H"))
+ if self.length > 0xE:
+ _form_factors = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'SIMM',
+ 0x04: 'SIP',
+ 0x05: 'Chip',
+ 0x06: 'DIP',
+ 0x07: 'ZIP',
+ 0x08: 'Proprietary Card',
+ 0x09: 'DIMM',
+ 0x0A: 'TSOP',
+ 0x0B: 'Row of chips',
+ 0x0C: 'RIMM',
+ 0x0D: 'SODIMM',
+ 0x0E: 'SRIMM',
+ 0x0F: 'FB-DIMM'
+ }
+ self.add_field('form_factor', u.unpack_one("B"), unpack.format_table("{}", _form_factors))
+ if self.length > 0xF:
+ self.add_field('device_set', u.unpack_one("B"))
+ if self.length > 0x10:
+ self.add_field('device_locator', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x11:
+ self.add_field('bank_locator', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x12:
+ _memory_types = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'DRAM',
+ 0x04: 'EDRAM',
+ 0x05: 'VRAM',
+ 0x06: 'SRAM',
+ 0x07: 'RAM',
+ 0x08: 'ROM',
+ 0x09: 'FLASH',
+ 0x0A: 'EEPROM',
+ 0x0B: 'FEPROM',
+ 0x0C: 'EPROM',
+ 0x0D: 'CDRAM',
+ 0x0E: '3DRAM',
+ 0x0F: 'SDRAM',
+ 0x10: 'SGRAM',
+ 0x11: 'RDRAM',
+ 0x12: 'DDR',
+ 0x13: 'DDR2',
+ 0x14: 'DDR2 FB-DIMM',
+ xrange(0x15, 0x17): 'Reserved',
+ 0x18: 'DDR3',
+ 0x19: 'FBD2'
+ }
+ self.add_field('memory_type', u.unpack_one("B"), unpack.format_table("{}", _memory_types))
+ if self.length > 0x13:
+ self.add_field('type_detail', u.unpack_one('<H'))
+ if self.length > 0x15:
+ self.add_field('speed', u.unpack_one("<H"))
+ if self.length > 0x17:
+ self.add_field('manufacturer', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x18:
+ self.add_field('serial_number', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x19:
+ self.add_field('asset_tag', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x1A:
+ self.add_field('part_number', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x1B:
+ self.add_field('attributes', u.unpack_one("B"))
+ self.add_field('rank', bitfields.getbits(self.attributes, 3, 0), "attributes[3:0]={}")
+ if self.length > 0x1C:
+ if self.size == 0x7FFF:
+ self.add_field('extended_size', u.unpack_one('<I'))
+ self.add_field('mem_size', bitfields.getbits(self.type_detail, 30, 0), "type_detail[30:0]={}")
+ else:
+ u.skip(4)
+ if self.length > 0x20:
+ self.add_field('configured_memory_clock_speed', u.unpack_one("<H"))
+ if self.length > 0x22:
+ self.add_field('minimum_voltage', u.unpack_one("<H"))
+ if self.length > 0x24:
+ self.add_field('maximum_voltage', u.unpack_one("<H"))
+ if self.length > 0x26:
+ self.add_field('configured_voltage', u.unpack_one("<H"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing MemoryDevice"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class MemoryErrorInfo32Bit(SmbiosBaseStructure):
+ smbios_structure_type = 18
+
+ def __init__(self, u, sm):
+ super(MemoryErrorInfo32Bit, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ _error_types = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'OK',
+ 0x04: 'Bad read',
+ 0x05: 'Parity error',
+ 0x06: 'Single-bit error',
+ 0x07: 'Double-bit error',
+ 0x08: 'Multi-bit error',
+ 0x09: 'Nibble error',
+ 0x0A: 'Checksum error',
+ 0x0B: 'CRC error',
+ 0x0C: 'Corrected single-bit error',
+ 0x0D: 'Corrected error',
+ 0x0E: 'Uncorrectable error'
+ }
+ self.add_field('error_type', u.unpack_one("B"), unpack.format_table("{}", _error_types))
+ if self.length > 0x5:
+ _error_granularity_field = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Device level',
+ 0x04: 'Memory partition level'
+ }
+ self.add_field('error_granularity', u.unpack_one("B"), unpack.format_table("{}", _error_granularity_field))
+ if self.length > 0x6:
+ _error_operation_field = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Read',
+ 0x04: 'Write',
+ 0x05: 'Partial write'
+ }
+ self.add_field('error_operation', u.unpack_one("B"), unpack.format_table("{}", _error_operation_field))
+ if self.length > 0x7:
+ self.add_field('vendor_syndrome', u.unpack_one("<I"))
+ if self.length > 0xB:
+ self.add_field('memory_array_error_address', u.unpack_one("<I"))
+ if self.length > 0xF:
+ self.add_field('device_error_address', u.unpack_one("<I"))
+ if self.length > 0x13:
+ self.add_field('error_resolution', u.unpack_one("<I"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing MemoryErrorInfo32Bit"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class MemoryArrayMappedAddress(SmbiosBaseStructure):
+ smbios_structure_type = 19
+
+ def __init__(self, u, sm):
+ super(MemoryArrayMappedAddress, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('starting_address', u.unpack_one("<I"))
+ # if FFFF FFFF: address stored in Extended Starting Address
+ if self.length > 0x8:
+ self.add_field('ending_address', u.unpack_one("<I"))
+ if self.length > 0xC:
+ self.add_field('memory_array_handle', u.unpack_one("<H"))
+ if self.length > 0xE:
+ self.add_field('partition_width', u.unpack_one("B"))
+ if self.length > 0xF:
+ # valid if starting_address = FFFF FFFF
+ if self.starting_address == 0xFFFFFFFF:
+ self.add_field('extended_starting_address', u.unpack_one("<Q"))
+ if self.length > 0x17:
+ self.add_field('extended_ending_address', u.unpack_one("<Q"))
+ else:
+ u.skip(16)
+
+ except:
+ self.decodeFailure = True
+ print "Error parsing MemoryArrayMappedAddress"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class MemoryDeviceMappedAddress(SmbiosBaseStructure):
+ smbios_structure_type = 20
+
+ def __init__(self, u, sm):
+ super(MemoryDeviceMappedAddress, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('starting_address', u.unpack_one("<I"))
+ # if FFFF FFFF: address stored in Extended Starting Address
+ if self.length > 0x8:
+ self.add_field('ending_address', u.unpack_one("<I"))
+ if self.length > 0xC:
+ self.add_field('memory_device_handle', u.unpack_one("<H"))
+ if self.length > 0xE:
+ self.add_field('memory_array_mapped_address_handle', u.unpack_one("<H"))
+ if self.length > 0x10:
+ self.add_field('partition_row_position', u.unpack_one("B"))
+ if self.length > 0x11:
+ self.add_field('interleave_position', u.unpack_one("B"))
+ if self.length > 0x12:
+ self.add_field('interleave_data_depth', u.unpack_one("B"))
+ if self.length > 0x13:
+ # valid if starting_address = FFFF FFFF
+ if self.starting_address == 0xFFFFFFFF:
+ self.add_field('extended_starting_address', u.unpack_one("<Q"))
+ if self.length > 0x1B:
+ self.add_field('extended_ending_address', u.unpack_one("<Q"))
+ else:
+ u.skip(16)
+ except:
+ self.decodeFailure = True
+ print "Error parsing MemoryDeviceMappedAddress"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class BuiltInPointingDevice(SmbiosBaseStructure):
+ smbios_structure_type = 21
+
+ def __init__(self, u, sm):
+ super(BuiltInPointingDevice, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ _pointing_device_types = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Mouse',
+ 0x04: 'Track Ball',
+ 0x05: 'Track Point',
+ 0x06: 'Glide Point',
+ 0x07: 'Touch Pad',
+ 0x08: 'Touch Screen',
+ 0x09: 'Optical Sensor'
+ }
+ self.add_field('pointing_device_type', u.unpack_one("B"), unpack.format_table("{}", _pointing_device_types))
+ if self.length > 0x5:
+ _interfaces = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Serial',
+ 0x04: 'PS/2',
+ 0x05: 'Infared',
+ 0x06: 'HP-HIL',
+ 0x07: 'Bus mouse',
+ 0x08: 'ADB (Apple Desktop Bus)',
+ 0x09: 'Bus mouse DB-9',
+ 0x0A: 'Bus mouse micro-DIN',
+ 0x0B: 'USB'
+ }
+ self.add_field('interface', u.unpack_one("B"), unpack.format_table("{}", _interfaces))
+ if self.length > 0x6:
+ self.add_field('num_buttons', u.unpack_one("B"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing BuiltInPointingDevice"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class PortableBattery(SmbiosBaseStructure):
+ smbios_structure_type = 22
+
+ def __init__(self, u, sm):
+ super(PortableBattery, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('location', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x5:
+ self.add_field('manufacturer', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x6:
+ self.add_field('manufacturer_date', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x7:
+ self.add_field('serial_number', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x8:
+ self.add_field('device_name', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x9:
+ _device_chemistry = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Lead Acid',
+ 0x04: 'Nickel Cadmium',
+ 0x05: 'Nickel metal hydride',
+ 0x06: 'Lithium-ion',
+ 0x07: 'Zinc air',
+ 0x08: 'Lithium Polymer'
+ }
+ self.add_field('device_chemistry', u.unpack_one("B"), unpack.format_table("{}", _device_chemistry))
+ if self.length > 0xA:
+ self.add_field('design_capacity', u.unpack_one("<H"))
+ if self.length > 0xC:
+ self.add_field('design_voltage', u.unpack_one("<H"))
+ if self.length > 0xE:
+ self.add_field('sbds_version_number', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0xF:
+ self.add_field('max_error_battery_data', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x10:
+ if self.serial_number == 0:
+ self.add_field('sbds_serial_number', u.unpack_one("<H"))
+ else:
+ u.skip(2)
+ if self.length > 0x12:
+ if self.manufacturer_date == 0:
+ self.add_field('sbds_manufacture_date', u.unpack_one("<H"))
+ self.add_field('year_biased_by_1980', bitfields.getbits(self.sbds_manufacture_date, 15, 9), "sbds_manufacture_date[15:9]={}")
+ self.add_field('month', bitfields.getbits(self.sbds_manufacture_date, 8, 5), "sbds_manufacture_date[8:5]={}")
+ self.add_field('date', bitfields.getbits(self.sbds_manufacture_date, 4, 0), "sbds_manufacture_date[4:0]={}")
+ else:
+ u.skip(2)
+ if self.length > 0x14:
+ if self.device_chemistry == 0x02:
+ self.add_field('sbds_device_chemistry', u.unpack_one("B"), self.fmtstr)
+ else:
+ u.skip(1)
+ if self.length > 0x15:
+ self.add_field('design_capacity_multiplier', u.unpack_one("B"))
+ if self.length > 0x16:
+ self.add_field('oem_specific', u.unpack_one("<I"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing PortableBattery"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class SystemReset(SmbiosBaseStructure):
+ smbios_structure_type = 23
+
+ def __init__(self, u, sm):
+ super(SystemReset, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('capabilities', u.unpack_one("B"))
+ self.add_field('contains_watchdog_timer', bool(bitfields.getbits(self.capabilities, 5)), "capabilities[5]={}")
+ _boot_option = {
+ 0b00: 'Reserved, do not use',
+ 0b01: 'Operating System',
+ 0b10: 'System utilities',
+ 0b11: 'Do not reboot'
+ }
+ self.add_field('boot_option_on_limit', bitfields.getbits(self.capabilities, 4, 3), unpack.format_table("capabilities[4:3]={}", _boot_option))
+ self.add_field('boot_option_after_watchdog_reset', bitfields.getbits(self.capabilities, 2, 1), unpack.format_table("capabilities[2:1]={}", _boot_option))
+ self.add_field('system_reset_enabled_by_user', bool(bitfields.getbits(self.capabilities, 0)), "capabilities[0]={}")
+ if self.length > 0x5:
+ self.add_field('reset_count', u.unpack_one("<H"))
+ if self.length > 0x5:
+ self.add_field('reset_limit', u.unpack_one("<H"))
+ if self.length > 0x9:
+ self.add_field('timer_interval', u.unpack_one("<H"))
+ if self.length > 0xB:
+ self.add_field('timeout', u.unpack_one("<H"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing SystemReset"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class HardwareSecurity(SmbiosBaseStructure):
+ smbios_structure_type = 24
+
+ def __init__(self, u, sm):
+ super(HardwareSecurity, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('hardware_security_settings', u.unpack_one("B"))
+ _status = {
+ 0x00: 'Disabled',
+ 0x01: 'Enabled',
+ 0x02: 'Not Implemented',
+ 0x03: 'Unknown'
+ }
+ self.add_field('power_on_password_status', bitfields.getbits(self.hardware_security_settings, 7, 6), unpack.format_table("hardware_security_settings[7:6]={}", _status))
+ self.add_field('keyboard_password_status', bitfields.getbits(self.hardware_security_settings, 5, 4), unpack.format_table("hardware_security_settings[5:4]={}", _status))
+ self.add_field('admin_password_status', bitfields.getbits(self.hardware_security_settings, 3, 2), unpack.format_table("hardware_security_settings0[3:2]={}", _status))
+ self.add_field('front_panel_reset_status', bitfields.getbits(self.hardware_security_settings, 1, 0), unpack.format_table("hardware_security_settings[1:0]={}", _status))
+ except:
+ self.decodeFailure = True
+ print "Error parsing HardwareSecurity"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class SystemPowerControls(SmbiosBaseStructure):
+ smbios_structure_type = 25
+
+ def __init__(self, u, sm):
+ super(SystemPowerControls, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('next_scheduled_poweron_month', u.unpack_one("B"))
+ self.add_field('next_scheduled_poweron_day_of_month', u.unpack_one("B"))
+ self.add_field('next_scheduled_poweron_hour', u.unpack_one("B"))
+ self.add_field('next_scheduled_poweron_minute', u.unpack_one("B"))
+ self.add_field('next_scheduled_poweron_second', u.unpack_one("B"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing SystemPowerControls"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class VoltageProbe(SmbiosBaseStructure):
+ smbios_structure_type = 26
+
+ def __init__(self, u, sm):
+ super(VoltageProbe, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('description', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x5:
+ self.add_field('location_and_status', u.unpack_one("B"))
+ _status = {
+ 0b001: 'Other',
+ 0b010: 'Unknown',
+ 0b011: 'OK',
+ 0b100: 'Non-critical',
+ 0b101: 'Critical',
+ 0b110: 'Non-recoverable'
+ }
+ _location = {
+ 0b00001: 'Other',
+ 0b00010: 'Unknown',
+ 0b00011: 'Processor',
+ 0b00100: 'Disk',
+ 0b00101: 'Peripheral Bay',
+ 0b00110: 'System Management Module',
+ 0b00111: 'Motherboard',
+ 0b01000: 'Memory Module',
+ 0b01001: 'Processor Module',
+ 0b01010: 'Power Unit',
+ 0b01011: 'Add-in Card'
+ }
+ self.add_field('status', bitfields.getbits(self.location_and_status, 7, 5), unpack.format_table("location_and_status[7:5]={}", _status))
+ self.add_field('location', bitfields.getbits(self.location_and_status, 4, 0), unpack.format_table("location_and_status[4:0]={}", _location))
+ if self.length > 0x6:
+ self.add_field('max_value', u.unpack_one("<H"))
+ if self.length > 0x8:
+ self.add_field('min_value', u.unpack_one("<H"))
+ if self.length > 0xA:
+ self.add_field('resolution', u.unpack_one("<H"))
+ if self.length > 0xC:
+ self.add_field('tolerance', u.unpack_one("<H"))
+ if self.length > 0xE:
+ self.add_field('accuracy', u.unpack_one("<H"))
+ if self.length > 0x10:
+ self.add_field('oem_defined', u.unpack_one("<I"))
+ if self.length > 0x14:
+ self.add_field('nominal_value', u.unpack_one("<H"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing VoltageProbe"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class CoolingDevice(SmbiosBaseStructure):
+ smbios_structure_type = 27
+
+ def __init__(self, u, sm):
+ super(CoolingDevice, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('temperature_probe_handle', u.unpack_one("<H"))
+ if self.length > 0x6:
+ self.add_field('device_type_and_status', u.unpack_one("B"))
+ _status = {
+ 0b001: 'Other',
+ 0b010: 'Unknown',
+ 0b011: 'OK',
+ 0b100: 'Non-critical',
+ 0b101: 'Critical',
+ 0b110: 'Non-recoverable'
+ }
+ _type = {
+ 0b00001: 'Other',
+ 0b00010: 'Unknown',
+ 0b00011: 'Fan',
+ 0b00100: 'Centrifugal Blower',
+ 0b00101: 'Chip Fan',
+ 0b00110: 'Cabinet Fan',
+ 0b00111: 'Power Supply Fan',
+ 0b01000: 'Heat Pipe',
+ 0b01001: 'Integrated Refrigeration',
+ 0b10000: 'Active Cooling',
+ 0b10001: 'Passive Cooling'
+ }
+ self.add_field('status', bitfields.getbits(self.device_type_and_status, 7, 5), unpack.format_table("device_type_and_status[7:5]={}", _status))
+ self.add_field('device_type', bitfields.getbits(self.device_type_and_status, 4, 0), unpack.format_table("device_type_and_status[4:0]={}", _type))
+ if self.length > 0x7:
+ self.add_field('cooling_unit_group', u.unpack_one("B"))
+ if self.length > 0x8:
+ self.add_field('OEM_defined', u.unpack_one("<I"))
+ if self.length > 0xC:
+ self.add_field('nominal_speed', u.unpack_one("<H"))
+ if self.length > 0xE:
+ self.add_field('description', u.unpack_one("B"), self.fmtstr)
+ except:
+ self.decodeFailure = True
+ print "Error parsing CoolingDevice"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class TemperatureProbe(SmbiosBaseStructure):
+ smbios_structure_type = 28
+
+ def __init__(self, u, sm):
+ super(TemperatureProbe, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('description', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x5:
+ self.add_field('location_and_status', u.unpack_one("B"))
+ _status = {
+ 0b001: 'Other',
+ 0b010: 'Unknown',
+ 0b011: 'OK',
+ 0b100: 'Non-critical',
+ 0b101: 'Critical',
+ 0b110: 'Non-recoverable'
+ }
+ _location = {
+ 0b00001: 'Other',
+ 0b00010: 'Unknown',
+ 0b00011: 'Processor',
+ 0b00100: 'Disk',
+ 0b00101: 'Peripheral Bay',
+ 0b00110: 'System Management Module',
+ 0b00111: 'Motherboard',
+ 0b01000: 'Memory Module',
+ 0b01001: 'Processor Module',
+ 0b01010: 'Power Unit',
+ 0b01011: 'Add-in Card',
+ 0b01100: 'Front Panel Board',
+ 0b01101: 'Back Panel Board',
+ 0b01110: 'Power System Board',
+ 0b01111: 'Drive Back Plane'
+ }
+ self.add_field('status', bitfields.getbits(self.location_and_status, 7, 5), unpack.format_table("location_and_status[7:5]={}", _status))
+ self.add_field('location', bitfields.getbits(self.location_and_status, 4, 0), unpack.format_table("location_and_status[4:0]={}", _location))
+ if self.length > 0x6:
+ self.add_field('maximum_value', u.unpack_one("<H"))
+ if self.length > 0x8:
+ self.add_field('minimum_value', u.unpack_one("<H"))
+ if self.length > 0xA:
+ self.add_field('resolution', u.unpack_one("<H"))
+ if self.length > 0xC:
+ self.add_field('tolerance', u.unpack_one("<H"))
+ if self.length > 0xE:
+ self.add_field('accuracy', u.unpack_one("<H"))
+ if self.length > 0x10:
+ self.add_field('OEM_defined', u.unpack_one("<I"))
+ if self.length > 0x14:
+ self.add_field('nominal_value', u.unpack_one("<H"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing TemperatureProbe"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class ElectricalCurrentProbe(SmbiosBaseStructure):
+ smbios_structure_type = 29
+
+ def __init__(self, u, sm):
+ super(ElectricalCurrentProbe, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('description', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x5:
+ self.add_field('location_and_status', u.unpack_one("B"))
+ _status = {
+ 0b001: 'Other',
+ 0b010: 'Unknown',
+ 0b011: 'OK',
+ 0b100: 'Non-critical',
+ 0b101: 'Critical',
+ 0b110: 'Non-recoverable'
+ }
+ _location = {
+ 0b00001: 'Other',
+ 0b00010: 'Unknown',
+ 0b00011: 'Processor',
+ 0b00100: 'Disk',
+ 0b00101: 'Peripheral Bay',
+ 0b00110: 'System Management Module',
+ 0b00111: 'Motherboard',
+ 0b01000: 'Memory Module',
+ 0b01001: 'Processor Module',
+ 0b01010: 'Power Unit',
+ 0b01011: 'Add-in Card',
+ 0b01100: 'Front Panel Board',
+ 0b01101: 'Back Panel Board',
+ 0b01110: 'Power System Board',
+ 0b01111: 'Drive Back Plane'
+ }
+ self.add_field('status', bitfields.getbits(self.location_and_status, 7, 5), unpack.format_table("location_and_status[7:5]={}", _status))
+ self.add_field('location', bitfields.getbits(self.location_and_status, 4, 0), unpack.format_table("location_and_status[4:0]={}", _location))
+ if self.length > 0x6:
+ self.add_field('maximum_value', u.unpack_one("<H"))
+ if self.length > 0x8:
+ self.add_field('minimum_value', u.unpack_one("<H"))
+ if self.length > 0xA:
+ self.add_field('resolution', u.unpack_one("<H"))
+ if self.length > 0xC:
+ self.add_field('tolerance', u.unpack_one("<H"))
+ if self.length > 0xE:
+ self.add_field('accuracy', u.unpack_one("<H"))
+ if self.length > 0x10:
+ self.add_field('OEM_defined', u.unpack_one("<I"))
+ if self.length > 0x14:
+ self.add_field('nominal_value', u.unpack_one("<H"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing ElectricalCurrentProbe"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class OutOfBandRemoteAccess(SmbiosBaseStructure):
+ smbios_structure_type = 30
+
+ def __init__(self, u, sm):
+ super(OutOfBandRemoteAccess, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('manufacturer_name', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x5:
+ self.add_field('connections', u.unpack_one("B"))
+ self.add_field('outbound_connection_enabled', bool(bitfields.getbits(self.connections, 1)), "connections[1]={}")
+ self.add_field('inbound_connection_enabled', bool(bitfields.getbits(self.connections, 0)), "connections[0]={}")
+ except:
+ self.decodeFailure = True
+ print "Error parsing OutOfBandRemoteAccess"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class BootIntegrityServicesEntryPoint(SmbiosBaseStructure):
+ smbios_structure_type = 31
+
+class SystemBootInformation(SmbiosBaseStructure):
+ smbios_structure_type = 32
+
+ def __init__(self, u, sm):
+ super(SystemBootInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0xA:
+ u.skip(6)
+ _boot_status = {
+ 0: 'No errors detected',
+ 1: 'No bootable media',
+ 2: '"normal" operating system failed to load',
+ 3: 'Firmware-detected hardware failure, including "unknown" failure types',
+ 4: 'Operating system-detected hardware failure',
+ 5: 'User-requested boot, usually through a keystroke',
+ 6: 'System security violation',
+ 7: 'Previously-requested image',
+ 8: 'System watchdog timer expired, causing the system to reboot',
+ xrange(9,127): 'Reserved for future assignment',
+ xrange(128, 191): 'Vendor/OEM-specific implementations',
+ xrange(192, 255): 'Product-specific implementations'
+ }
+ self.add_field('boot_status', u.unpack_one("B"), unpack.format_table("{}", _boot_status))
+ except:
+ self.decodeFailure = True
+ print "Error parsing SystemBootInformation"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class MemoryErrorInfo64Bit(SmbiosBaseStructure):
+ smbios_structure_type = 33
+
+ def __init__(self, u, sm):
+ super(MemoryErrorInfo64Bit, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ _error_types = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'OK',
+ 0x04: 'Bad read',
+ 0x05: 'Parity error',
+ 0x06: 'Single-bit error',
+ 0x07: 'Double-bit error',
+ 0x08: 'Multi-bit error',
+ 0x09: 'Nibble error',
+ 0x0A: 'Checksum error',
+ 0x0B: 'CRC error',
+ 0x0C: 'Corrected single-bit error',
+ 0x0D: 'Corrected error',
+ 0x0E: 'Uncorrectable error'
+ }
+ self.add_field('error_type', u.unpack_one("B"), unpack.format_table("{}", _error_types))
+ if self.length > 0x5:
+ _error_granularity_field = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Device level',
+ 0x04: 'Memory partition level'
+ }
+ self.add_field('error_granularity', u.unpack_one("B"), unpack.format_table("{}", _error_granularity_field))
+ if self.length > 0x6:
+ _error_operation_field = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Read',
+ 0x04: 'Write',
+ 0x05: 'Partial write'
+ }
+ self.add_field('error_operation', u.unpack_one("B"), unpack.format_table("{}", _error_operation_field))
+ if self.length > 0x7:
+ self.add_field('vendor_syndrome', u.unpack_one("<I"))
+ if self.length > 0xB:
+ self.add_field('memory_array_error_address', u.unpack_one("<Q"))
+ if self.length > 0xF:
+ self.add_field('device_error_address', u.unpack_one("<Q"))
+ if self.length > 0x13:
+ self.add_field('error_resolution', u.unpack_one("<Q"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing MemoryErrorInfo64Bit"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class ManagementDevice(SmbiosBaseStructure):
+ smbios_structure_type = 34
+
+ def __init__(self, u, sm):
+ super(ManagementDevice, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('description', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x5:
+ _type = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'National Semiconductor LM75',
+ 0x04: 'National Semiconductor LM78',
+ 0x05: 'National Semiconductor LM79',
+ 0x06: 'National Semiconductor LM80',
+ 0x07: 'National Semiconductor LM81',
+ 0x08: 'Analog Devices ADM9240',
+ 0x09: 'Dallas Semiconductor DS1780',
+ 0x0A: 'Maxim 1617',
+ 0x0B: 'Genesys GL518SM',
+ 0x0C: 'Winbond W83781D',
+ 0x0D: 'Holtek HT82H791'
+ }
+ self.add_field('device_type', u.unpack_one("B"), unpack.format_table("{}", _type))
+ if self.length > 0x6:
+ self.add_field('address', u.unpack_one("<I"))
+ if self.length > 0xA:
+ _address_type = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'I/O Port',
+ 0x04: 'Memory',
+ 0x05: 'SM Bus'
+ }
+ self.add_field('address_type', u.unpack_one("B"), unpack.format_table("{}", _address_type))
+ except:
+ self.decodeFailure = True
+ print "Error parsing ManagementDevice"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class ManagementDeviceComponent(SmbiosBaseStructure):
+ smbios_structure_type = 35
+
+ def __init__(self, u, sm):
+ super(ManagementDeviceComponent, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('description', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x5:
+ self.add_field('management_device_handle', u.unpack_one("<H"))
+ if self.length > 0x7:
+ self.add_field('component_handle', u.unpack_one("<H"))
+ if self.length > 0x9:
+ self.add_field('threshold_handle', u.unpack_one("<H"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing ManagementDeviceComponent"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class ManagementDeviceThresholdData(SmbiosBaseStructure):
+ smbios_structure_type = 36
+
+ def __init__(self, u, sm):
+ super(ManagementDeviceThresholdData, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('lower_threshold_noncritical', u.unpack_one("<H"))
+ if self.length > 0x6:
+ self.add_field('upper_threshold_noncritical', u.unpack_one("<H"))
+ if self.length > 0x8:
+ self.add_field('lower_threshold_critical', u.unpack_one("<H"))
+ if self.length > 0xA:
+ self.add_field('upper_threshold_critical', u.unpack_one("<H"))
+ if self.length > 0xC:
+ self.add_field('lower_threshold_nonrecoverable', u.unpack_one("<H"))
+ if self.length > 0xE:
+ self.add_field('upper_threshold_nonrecoverable', u.unpack_one("<H"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing ManagementDeviceThresholdData"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class MemoryChannel(SmbiosBaseStructure):
+ smbios_structure_type = 37
+
+ def __init__(self, u, sm):
+ super(MemoryChannel, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ _channel_type = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'RamBus',
+ 0x04: 'SyncLink'
+ }
+ self.add_field('channel_type', u.unpack_one("B"), unpack.format_table("{}", _channel_type))
+ if self.length > 0x6:
+ self.add_field('max_channel_load', u.unpack_one("B"))
+ if self.length > 0x8:
+ self.add_field('memory_device_count', u.unpack_one("B"))
+ if self.length > 0xA:
+ self.add_field('memory_device_load', u.unpack_one("B"))
+ if self.length > 0xC:
+ self.add_field('memory_device_handle', u.unpack_one("<H"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing MemoryChannel"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class IPMIDeviceInformation(SmbiosBaseStructure):
+ smbios_structure_type = 38
+
+ def __init__(self, u, sm):
+ super(IPMIDeviceInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ _interface_type = {
+ 0x00: 'Unknown',
+ 0x01: 'KCS: Keyboard Controller Style',
+ 0x02: 'SMIC: Server Management Interface Chip',
+ 0x03: 'BT: Block Transfer',
+ xrange(0x04, 0xFF): 'Reserved'
+ }
+ self.add_field('interface_type', u.unpack_one("B"), unpack.format_table("{}", _interface_type))
+ self.add_field('ipmi_specification_revision', u.unpack_one("B"))
+ self.add_field('msd_revision', bitfields.getbits(self.ipmi_specification_revision, 7, 4), "ipmi_specification_revision[7:4]={}")
+ self.add_field('lsd_revision', bitfields.getbits(self.ipmi_specification_revision, 3, 0), "ipmi_specification_revision[3:0]={}")
+
+ self.add_field('i2c_slave_address', u.unpack_one("B"))
+ self.add_field('nv_storage_device_address', u.unpack_one("B"))
+ self.add_field('base_address', u.unpack_one("<Q"))
+ # if lsb is 1, address is in IO space. otherwise, memory-mapped
+ self.add_field('base_address_modifier_interrupt_info', u.unpack_one("B"))
+ _reg_spacing = {
+ 0b00: 'Interface registers are on successive byte boundaries',
+ 0b01: 'Interface registers are on 32-bit boundaries',
+ 0b10: 'Interface registers are on 16-byte boundaries',
+ 0b11: 'Reserved'
+ }
+ self.add_field('register_spacing', bitfields.getbits(self.base_address_modifier_interrupt_info, 7, 6), unpack.format_table("base_address_modifier_interrupt_info[7:6]={}", _reg_spacing))
+ self.add_field('ls_bit_for_addresses', bitfields.getbits(self.base_address_modifier_interrupt_info, 4), "base_address_modifier_interrupt_info[4]={}")
+ self.add_field('interrupt_info_specified', bool(bitfields.getbits(self.base_address_modifier_interrupt_info, 3)), "base_address_modifier_interrupt_info[3]={}")
+ _polarity = {
+ 0: 'active low',
+ 1: 'active high'
+ }
+ self.add_field('interrupt_polarity', bitfields.getbits(self.base_address_modifier_interrupt_info, 1), unpack.format_table("base_address_modifier_interrupt_info[1]={}", _polarity))
+ _interrupt_trigger = {
+ 0: 'edge',
+ 1: 'level'
+ }
+ self.add_field('interrupt_trigger_mode', bitfields.getbits(self.base_address_modifier_interrupt_info, 0), unpack.format_table("base_address_modifier_interrupt_info[0]={}", _interrupt_trigger))
+ self.add_field('interrupt_number', u.unpack_one("B"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing IPMIDeviceInformation"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class SystemPowerSupply(SmbiosBaseStructure):
+ smbios_structure_type = 39
+
+ def __init__(self, u, sm):
+ super(SystemPowerSupply, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('power_unit_group', u.unpack_one("B"))
+ if self.length > 0x5:
+ self.add_field('location', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x6:
+ self.add_field('device_name', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x7:
+ self.add_field('manufacturer', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x8:
+ self.add_field('serial_number', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x9:
+ self.add_field('asset_tag', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0xA:
+ self.add_field('model_part_number', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0xB:
+ self.add_field('revision_level', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0xC:
+ self.add_field('max_power_capacity', u.unpack_one("<H"))
+ if self.length > 0xE:
+ self.add_field('power_supply_characteristics', u.unpack_one("<H"))
+ _dmtf_power_supply_type = {
+ 0b001: 'Other',
+ 0b010: 'Unknown',
+ 0b011: 'Linear',
+ 0b100: 'Switching',
+ 0b101: 'Battery',
+ 0b110: 'UPS',
+ 0b111: 'Converter',
+ 0b1000: 'Regulator',
+ xrange(0b1001, 0b1111): 'Reserved'
+ }
+ self.add_field('dmtf_power_supply_type', bitfields.getbits(self.power_supply_characteristics, 13, 10), unpack.format_table("power_supply_characteristics[13:10]={}", _dmtf_power_supply_type))
+ _status = {
+ 0b001: 'Other',
+ 0b010: 'Unknown',
+ 0b011: 'OK',
+ 0b100: 'Non-critical',
+ 0b101: 'Critical; power supply has failed and has been taken off-line'
+ }
+ self.add_field('status', bitfields.getbits(self.power_supply_characteristics, 9, 7), unpack.format_table("power_supply_characteristics[9:7]={}", _status))
+ _dmtf_input_voltage_range_switching = {
+ 0b001: 'Other',
+ 0b010: 'Unknown',
+ 0b011: 'Manual',
+ 0b100: 'Auto-switch',
+ 0b101: 'Wide range',
+ 0b110: 'Not applicable',
+ xrange(0b0111, 0b1111): 'Reserved'
+ }
+ self.add_field('dmtf_input_voltage_range_switching', bitfields.getbits(self.power_supply_characteristics, 6, 3), unpack.format_table("power_supply_characteristics[6:3]={}", _dmtf_input_voltage_range_switching))
+ self.add_field('power_supply_unplugged', bool(bitfields.getbits(self.power_supply_characteristics, 2)), "power_supply_characteristics[2]={}")
+ self.add_field('power_supply_present', bool(bitfields.getbits(self.power_supply_characteristics, 1)), "power_supply_characteristics[1]={}")
+ self.add_field('power_supply_hot_replaceable', bool(bitfields.getbits(self.power_supply_characteristics, 0)), "power_supply_characteristics[0]={}")
+ if self.length > 0x10:
+ self.add_field('input_voltage_probe_handle', u.unpack_one("<H"))
+ if self.length > 0x12:
+ self.add_field('cooling_device_handle', u.unpack_one("<H"))
+ if self.length > 0x14:
+ self.add_field('input_current_probe_handle', u.unpack_one("<H"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing SystemPowerSupply"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class AdditionalInformation(SmbiosBaseStructure):
+ smbios_structure_type = 40
+
+ def __init__(self, u, sm):
+ super(AdditionalInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('num_additional_information_entries', u.unpack_one("B"))
+ if self.length > 0x5:
+ self.add_field('additional_information_entry_length', u.unpack_one("B"))
+ self.add_field('referenced_handle', u.unpack_one("<H"))
+ self.add_field('referenced_offset', u.unpack_one("B"))
+ self.add_field('string', u.unpack_one("B"), self.fmtstr)
+ self.add_field('value', u.unpack_rest())
+ except:
+ self.decodeFailure = True
+ print "Error parsing AdditionalInformation"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class OnboardDevicesExtendedInformation(SmbiosBaseStructure):
+ smbios_structure_type = 41
+
+ def __init__(self, u, sm):
+ super(OnboardDevicesExtendedInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('reference_designation', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x5:
+ self.add_field('device_type', u.unpack_one("B"))
+ self.add_field('device_enabled', bool(bitfields.getbits(self.device_type, 7)), "device_type[7]={}")
+ _device_types = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Video',
+ 0x04: 'SCSI Controller',
+ 0x05: 'Ethernet',
+ 0x06: 'Token Ring',
+ 0x07: 'Sound',
+ 0x08: 'PATA Controller',
+ 0x09: 'SATA Controller',
+ 0x0A: 'SAS Controller'
+ }
+ self.add_field('type_of_device', bitfields.getbits(self.device_type, 6, 0), unpack.format_table("device_type[6:0]={}", _device_types))
+ if self.length > 0x6:
+ self.add_field('device_type_instance', u.unpack_one("B"))
+ if self.length > 0x7:
+ self.add_field('segment_group_number', u.unpack_one("<H"))
+ if self.length > 0x9:
+ self.add_field('bus_number', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0xA:
+ self.add_field('device_and_function_number', u.unpack_one("B"))
+ self.add_field('device_number', bitfields.getbits(self.device_type, 7, 3), "device_and_function_number[7:3]={}")
+ self.add_field('function_number', bitfields.getbits(self.device_type, 2, 0), "device_and_function_number[2:0]={}")
+ except:
+ self.decodeFailure = True
+ print "Error parsing OnboardDevicesExtendedInformation"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class ManagementControllerHostInterface(SmbiosBaseStructure):
+ smbios_structure_type = 42
+
+ def __init__(self, u, sm):
+ super(ManagementControllerHostInterface, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ _interface_types = {
+ 0x00: 'Reserved',
+ 0x01: 'Reserved',
+ 0x02: 'KCS: Keyboard Controller Style',
+ 0x03: '8250 UART Register Compatible',
+ 0x04: '16450 UART Register Compatible',
+ 0x05: '16550/16550A UART Register Compatible',
+ 0x06: '16650/16650A UART Register Compatible',
+ 0x07: '16750/16750A UART Register Compatible',
+ 0x08: '16850/16850A UART Register Compatible',
+ 0xF0: 'OEM'
+ }
+ self.add_field('interface_type', u.unpack_one("B"), unpack.format_table("{}", _interface_types))
+ if self.length > 0x5:
+ self.add_field('mc_host_interface_data', u.unpack_rest(), self.fmtstr)
+ except:
+ self.decodeFailure = True
+ print "Error parsing ManagementControllerHostInterface"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class Inactive(SmbiosBaseStructure):
+ smbios_structure_type = 126
+
+ def __init__(self, u, sm):
+ super(Inactive, self).__init__(u, sm)
+ self.fini()
+
+class EndOfTable(SmbiosBaseStructure):
+ smbios_structure_type = 127
+
+ def __init__(self, u, sm):
+ super(EndOfTable, self).__init__(u, sm)
+ self.fini()
+
+class SmbiosStructureUnknown(SmbiosBaseStructure):
+ smbios_structure_type = None
+
+ def __init__(self, u, sm):
+ super(SmbiosStructureUnknown, self).__init__(u, sm)
+ self.fini()
+
+_smbios_structures = [
+ BIOSInformation,
+ SystemInformation,
+ BaseboardInformation,
+ SystemEnclosure,
+ ProcessorInformation,
+ MemoryControllerInformation,
+ MemoryModuleInformation,
+ CacheInformation,
+ PortConnectorInfo,
+ SystemSlots,
+ OnBoardDevicesInformation,
+ OEMStrings,
+ SystemConfigOptions,
+ BIOSLanguageInformation,
+ GroupAssociations,
+ SystemEventLog,
+ PhysicalMemoryArray,
+ MemoryDevice,
+ MemoryErrorInfo32Bit,
+ MemoryArrayMappedAddress,
+ MemoryDeviceMappedAddress,
+ BuiltInPointingDevice,
+ PortableBattery,
+ SystemReset,
+ HardwareSecurity,
+ SystemPowerControls,
+ VoltageProbe,
+ CoolingDevice,
+ TemperatureProbe,
+ ElectricalCurrentProbe,
+ OutOfBandRemoteAccess,
+ BootIntegrityServicesEntryPoint,
+ SystemBootInformation,
+ MemoryErrorInfo64Bit,
+ ManagementDevice,
+ ManagementDeviceComponent,
+ ManagementDeviceThresholdData,
+ MemoryChannel,
+ IPMIDeviceInformation,
+ SystemPowerSupply,
+ AdditionalInformation,
+ OnboardDevicesExtendedInformation,
+ ManagementControllerHostInterface,
+ Inactive,
+ EndOfTable,
+ SmbiosStructureUnknown, # Must always come last
+]
+
+def log_smbios_info():
+ with redirect.logonly():
+ try:
+ sm = SMBIOS()
+ print
+ if sm is None:
+ print "No SMBIOS structures found"
+ return
+ output = {}
+ known_types = (0, 1)
+ for sm_struct in sm.structures:
+ if sm_struct.type in known_types:
+ output.setdefault(sm_struct.type, []).append(sm_struct)
+ if len(output) == len(known_types):
+ break
+
+ print "SMBIOS information:"
+ for key in sorted(known_types):
+ for s in output.get(key, ["No structure of type {} found".format(key)]):
+ print ttypager._wrap("{}: {}".format(key, s))
+ except:
+ print "Error parsing SMBIOS information:"
+ import traceback
+ traceback.print_exc()
+
+def dump_raw():
+ try:
+ sm = SMBIOS()
+ if sm:
+ s = "SMBIOS -- Raw bytes and structure decode.\n\n"
+
+ s += str(sm.header) + '\n'
+ s += bits.dumpmem(sm._header_memory) + '\n'
+
+ s += "Raw bytes for the SMBIOS structures\n"
+ s += bits.dumpmem(sm._structure_memory) + '\n'
+
+ for sm_struct in sm.structures:
+ s += str(sm_struct) + '\n'
+ s += bits.dumpmem(sm_struct.raw_data)
+
+ s += "Strings:\n"
+ for n in range(1, len(getattr(sm_struct, "strings", [])) + 1):
+ s += str(sm_struct.fmtstr(n)) + '\n'
+ s += bits.dumpmem(sm_struct.raw_strings) + '\n'
+ else:
+ s = "No SMBIOS structures found"
+ ttypager.ttypager_wrap(s, indent=False)
+ except:
+ print "Error parsing SMBIOS information:"
+ import traceback
+ traceback.print_exc()
+
+def dump():
+ try:
+ sm = SMBIOS()
+ if sm:
+ s = str(sm)
+ else:
+ s = "No SMBIOS structures found"
+ ttypager.ttypager_wrap(s, indent=False)
+ except:
+ print "Error parsing SMBIOS information:"
+ import traceback
+ traceback.print_exc()
+
+def annex_a_conformance():
+ try:
+ sm = SMBIOS()
+
+ # check: 1. The table anchor string "_SM_" is present in the address range 0xF0000 to 0xFFFFF on a 16-byte bound
+
+ def table_entry_point_verification():
+ ''' Verify table entry-point'''
+ if (sm.header.length < 0x1F):
+ print "Failure: Table entry-point - The entry-point Length must be at least 0x1F"
+ if sm.header.checksum != 0:
+ print "Failure: Table entry-point - The entry-point checksum must evaluate to 0"
+ if ((sm.header.major_version < 2) and (sm.header.minor_version < 4)):
+ print "Failure: Table entry-point - SMBIOS version must be at least 2.4"
+ if (sm.header.intermediate_anchor_string == '_DMI_'):
+ print "Failure: Table entry-point - The Intermediate Anchor String must be '_DMI_'"
+ if (sm.header.intermediate_checksum != 0):
+ print "Failure: Table entry-point - The Intermediate checksum must evaluate to 0"
+
+ #check: 3. The structure-table is traversable and conforms to the entry-point specifications:
+
+ def req_structures():
+ '''Checks for required structures and corresponding data'''
+ types_present = [sm.structures[x].smbios_structure_type for x in range(len(sm.structures))]
+ required = [0, 1, 4, 7, 9, 16, 17, 19, 31, 32]
+ for s in required:
+ if s not in set(types_present):
+ print "Failure: Type {} required but not found".format(s)
+
+ else:
+ if s == 0:
+ if types_present.count(s) > 1:
+ print "Failure: Type {} - One and only one structure of this type must be present.".format(s)
+ if sm.structure_type(s).length < 0x18:
+ print "Failure: Type {} - The structure Length field must be at least 0x18".format(s)
+ if sm.structure_type(s).version is None:
+ print "Failure: Type {} - BIOS Version string must be present and non-null.".format(s)
+ if sm.structure_type(s).release_date is None:
+ print "Failure: Type {} - BIOS Release Date string must be present, non-null, and include a 4-digit year".format(s)
+ if bitfields.getbits(sm.structure_type(s).characteristics, 3, 0) != 0 or bitfields.getbits(sm.structure_type(s).characteristics, 31, 4) == 0:
+ print "Failure: Type {} - BIOS Characteristics: bits 3:0 must all be 0, and at least one of bits 31:4 must be set to 1.".format(s)
+ elif s == 1:
+ if types_present.count(s) > 1:
+ print "Failure: Type {} - One and only one structure of this type must be present.".format(s)
+ if sm.structure_type(s).length < 0x1B:
+ print "Failure: Type {} - The structure Length field must be at least 0x1B".format(s)
+ if sm.structure_type(s).manufacturer == None:
+ print "Failure: Type {} - Manufacturer string must be present and non-null.".format(s)
+ if sm.structure_type(s).product_name == None:
+ print "Failure: Type {} - Product Name string must be present and non-null".format(s)
+ if sm.structure_type(s).uuid == '00000000 00000000' and sm.structure_type(s).uuid == 'FFFFFFFF FFFFFFFF':
+ print "Failure: Type {} - UUID field must be neither 00000000 00000000 nor FFFFFFFF FFFFFFFF.".format(s)
+ if sm.structure_type(s).wakeup_type == 00 and sm.structure_type(s).wakeup_type == 0x02:
+ print "Failure: Type {} - Wake-up Type field must be neither 00h (Reserved) nor 02h (Unknown).".format(s)
+ # continue for remaining required types
+
+ # check remaining conformance guidelines
+
+ table_entry_point_verification()
+ req_structures()
+ except:
+ print "Error checking ANNEX A conformance guidelines"
+ import traceback
+ traceback.print_exc()
diff --git a/tests/avocado/acpi-bits/bits-tests/smilatency.py2 b/tests/avocado/acpi-bits/bits-tests/smilatency.py2
new file mode 100644
index 0000000000..405af67e19
--- /dev/null
+++ b/tests/avocado/acpi-bits/bits-tests/smilatency.py2
@@ -0,0 +1,107 @@
+# Copyright (c) 2015, Intel Corporation
+# All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of Intel Corporation nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# This script runs only from the biosbits VM.
+
+"""SMI latency test."""
+
+import bits
+from collections import namedtuple
+import testsuite
+import time
+import usb
+
+def register_tests():
+ pass
+# testsuite.add_test("SMI latency test", smi_latency);
+# testsuite.add_test("SMI latency test with USB disabled via BIOS handoff", test_with_usb_disabled, runall=False);
+
+def smi_latency():
+ MSR_SMI_COUNT = 0x34
+
+ print "Warning: touching the keyboard can affect the results of this test."
+
+ tsc_per_sec = bits.tsc_per_sec()
+ tsc_per_usec = tsc_per_sec / (1000 * 1000)
+ bins = [long(tsc_per_usec * 10**i) for i in range(9)]
+ bin_descs = [
+ "0 < t <= 1us",
+ "1us < t <= 10us",
+ "10us < t <= 100us",
+ "100us < t <= 1ms",
+ "1ms < t <= 10ms",
+ "10ms < t <= 100ms",
+ "100ms < t <= 1s ",
+ "1s < t <= 10s ",
+ "10s < t <= 100s ",
+ "100s < t ",
+ ]
+
+ print "Starting test. Wait here, I will be back in 15 seconds."
+ (max_latency, smi_count_delta, bins) = bits.smi_latency(long(15 * tsc_per_sec), bins)
+ BinType = namedtuple('BinType', ("max", "total", "count", "times"))
+ bins = [BinType(*b) for b in bins]
+
+ testsuite.test("SMI latency < 150us to minimize risk of OS timeouts", max_latency / tsc_per_usec <= 150)
+ if not testsuite.show_detail():
+ return
+
+ for bin, desc in zip(bins, bin_descs):
+ if bin.count == 0:
+ continue
+ testsuite.print_detail("{}; average = {}; count = {}".format(desc, bits.format_tsc(bin.total/bin.count), bin.count))
+ deltas = (bits.format_tsc(t2 - t1) for t1,t2 in zip(bin.times, bin.times[1:]))
+ testsuite.print_detail(" Times between first few observations: {}".format(" ".join("{:>6}".format(delta) for delta in deltas)))
+
+ if smi_count_delta is not None:
+ testsuite.print_detail("{} SMI detected using MSR_SMI_COUNT (MSR {:#x})".format(smi_count_delta, MSR_SMI_COUNT))
+
+ testsuite.print_detail("Summary of impact: observed maximum latency = {}".format(bits.format_tsc(max_latency)))
+
+def test_with_usb_disabled():
+ if usb.handoff_to_os():
+ smi_latency()
+
+def average_io_smi(port, value, count):
+ def f():
+ tsc_start = bits.rdtsc()
+ bits.outb(port, value)
+ return bits.rdtsc() - tsc_start
+ counts = [f() for i in range(count)]
+ return sum(counts)/len(counts)
+
+def time_io_smi(port=0xb2, value=0, count=1000):
+ count_for_estimate = 10
+ start = time.time()
+ average_io_smi(port, value, count_for_estimate)
+ avg10 = time.time() - start
+ estimate = avg10 * count/count_for_estimate
+ if estimate > 1:
+ print "Running test, estimated time: {}s".format(int(estimate))
+ average = average_io_smi(port, value, count)
+ print "Average of {} SMIs (via outb, port={:#x}, value={:#x}): {}".format(count, port, value, bits.format_tsc(average))
diff --git a/tests/avocado/acpi-bits/bits-tests/testacpi.py2 b/tests/avocado/acpi-bits/bits-tests/testacpi.py2
new file mode 100644
index 0000000000..7bf9075c1b
--- /dev/null
+++ b/tests/avocado/acpi-bits/bits-tests/testacpi.py2
@@ -0,0 +1,287 @@
+# Copyright (c) 2015, Intel Corporation
+# All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of Intel Corporation nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# This script runs only from the biosbits VM.
+
+"""Tests for ACPI"""
+
+import acpi
+import bits
+import bits.mwait
+import struct
+import testutil
+import testsuite
+import time
+
+def register_tests():
+ testsuite.add_test("ACPI _MAT (Multiple APIC Table Entry) under Processor objects", test_mat, submenu="ACPI Tests")
+# testsuite.add_test("ACPI _PSS (Pstate) table conformance tests", test_pss, submenu="ACPI Tests")
+# testsuite.add_test("ACPI _PSS (Pstate) runtime tests", test_pstates, submenu="ACPI Tests")
+ testsuite.add_test("ACPI DSDT (Differentiated System Description Table)", test_dsdt, submenu="ACPI Tests")
+ testsuite.add_test("ACPI FACP (Fixed ACPI Description Table)", test_facp, submenu="ACPI Tests")
+ testsuite.add_test("ACPI HPET (High Precision Event Timer Table)", test_hpet, submenu="ACPI Tests")
+ testsuite.add_test("ACPI MADT (Multiple APIC Description Table)", test_apic, submenu="ACPI Tests")
+ testsuite.add_test("ACPI MPST (Memory Power State Table)", test_mpst, submenu="ACPI Tests")
+ testsuite.add_test("ACPI RSDP (Root System Description Pointer Structure)", test_rsdp, submenu="ACPI Tests")
+ testsuite.add_test("ACPI XSDT (Extended System Description Table)", test_xsdt, submenu="ACPI Tests")
+
+def test_mat():
+ cpupaths = acpi.get_cpupaths()
+ apic = acpi.parse_apic()
+ procid_apicid = apic.procid_apicid
+ uid_x2apicid = apic.uid_x2apicid
+ for cpupath in cpupaths:
+ # Find the ProcId defined by the processor object
+ processor = acpi.evaluate(cpupath)
+ # Find the UID defined by the processor object's _UID method
+ uid = acpi.evaluate(cpupath + "._UID")
+ mat_buffer = acpi.evaluate(cpupath + "._MAT")
+ if mat_buffer is None:
+ continue
+ # Process each _MAT subtable
+ mat = acpi._MAT(mat_buffer)
+ for index, subtable in enumerate(mat):
+ if subtable.subtype == acpi.MADT_TYPE_LOCAL_APIC:
+ if subtable.flags.bits.enabled:
+ testsuite.test("{} Processor declaration ProcId = _MAT ProcId".format(cpupath), processor.ProcId == subtable.proc_id)
+ testsuite.print_detail("{} ProcId ({:#02x}) != _MAT ProcId ({:#02x})".format(cpupath, processor.ProcId, subtable.proc_id))
+ testsuite.print_detail("Processor Declaration: {}".format(processor))
+ testsuite.print_detail("_MAT entry[{}]: {}".format(index, subtable))
+ if testsuite.test("{} with local APIC in _MAT has local APIC in MADT".format(cpupath), processor.ProcId in procid_apicid):
+ testsuite.test("{} ApicId derived using Processor declaration ProcId = _MAT ApicId".format(cpupath), procid_apicid[processor.ProcId] == subtable.apic_id)
+ testsuite.print_detail("{} ApicId derived from MADT ({:#02x}) != _MAT ApicId ({:#02x})".format(cpupath, procid_apicid[processor.ProcId], subtable.apic_id))
+ testsuite.print_detail("Processor Declaration: {}".format(processor))
+ testsuite.print_detail("_MAT entry[{}]: {}".format(index, subtable))
+ if subtable.subtype == acpi.MADT_TYPE_LOCAL_X2APIC:
+ if subtable.flags.bits.enabled:
+ if testsuite.test("{} with x2Apic in _MAT has _UID".format(cpupath), uid is not None):
+ testsuite.test("{}._UID = _MAT UID".format(cpupath), uid == subtable.uid)
+ testsuite.print_detail("{}._UID ({:#x}) != _MAT UID ({:#x})".format(cpupath, uid, subtable.uid))
+ testsuite.print_detail("_MAT entry[{}]: {}".format(index, subtable))
+ if testsuite.test("{} with _MAT x2Apic has x2Apic in MADT".format(cpupath), subtable.uid in uid_x2apicid):
+ testsuite.test("{} x2ApicId derived from MADT using UID = _MAT x2ApicId".format(cpupath), uid_x2apicid[subtable.uid] == subtable.x2apicid)
+ testsuite.print_detail("{} x2ApicId derived from MADT ({:#02x}) != _MAT x2ApicId ({:#02x})".format(cpupath, uid_x2apicid[subtable.uid], subtable.x2apicid))
+ testsuite.print_detail("_MAT entry[{}]: {}".format(index, subtable))
+
+def test_pss():
+ uniques = acpi.parse_cpu_method("_PSS")
+ # We special-case None here to avoid a double-failure for CPUs without a _PSS
+ testsuite.test("_PSS must be identical for all CPUs", len(uniques) <= 1 or (len(uniques) == 2 and None in uniques))
+ for pss, cpupaths in uniques.iteritems():
+ if not testsuite.test("_PSS must exist", pss is not None):
+ testsuite.print_detail(acpi.factor_commonprefix(cpupaths))
+ testsuite.print_detail('No _PSS exists')
+ continue
+
+ if not testsuite.test("_PSS must not be empty", pss.pstates):
+ testsuite.print_detail(acpi.factor_commonprefix(cpupaths))
+ testsuite.print_detail('_PSS is empty')
+ continue
+
+ testsuite.print_detail(acpi.factor_commonprefix(cpupaths))
+ for index, pstate in enumerate(pss.pstates):
+ testsuite.print_detail("P[{}]: {}".format(index, pstate))
+
+ testsuite.test("_PSS must contain at most 16 Pstates", len(pss.pstates) <= 16)
+ testsuite.test("_PSS must have no duplicate Pstates", len(pss.pstates) == len(set(pss.pstates)))
+
+ frequencies = [p.core_frequency for p in pss.pstates]
+ testsuite.test("_PSS must list Pstates in descending order of frequency", frequencies == sorted(frequencies, reverse=True))
+
+ testsuite.test("_PSS must have Pstates with no duplicate frequencies", len(frequencies) == len(set(frequencies)))
+
+ dissipations = [p.power for p in pss.pstates]
+ testsuite.test("_PSS must list Pstates in descending order of power dissipation", dissipations == sorted(dissipations, reverse=True))
+
+def test_pstates():
+ """Execute and verify frequency for each Pstate in the _PSS"""
+ IA32_PERF_CTL = 0x199
+ with bits.mwait.use_hint(), bits.preserve_msr(IA32_PERF_CTL):
+ cpupath_procid = acpi.find_procid()
+ cpupath_uid = acpi.find_uid()
+ apic = acpi.parse_apic()
+ procid_apicid = apic.procid_apicid
+ uid_x2apicid = apic.uid_x2apicid
+ def cpupath_apicid(cpupath):
+ if procid_apicid is not None:
+ procid = cpupath_procid.get(cpupath, None)
+ if procid is not None:
+ apicid = procid_apicid.get(procid, None)
+ if apicid is not None:
+ return apicid
+ if uid_x2apicid is not None:
+ uid = cpupath_uid.get(cpupath, None)
+ if uid is not None:
+ apicid = uid_x2apicid.get(uid, None)
+ if apicid is not None:
+ return apicid
+ return bits.cpus()[0]
+
+ bclk = testutil.adjust_to_nearest(bits.bclk(), 100.0/12) * 1000000
+
+ uniques = acpi.parse_cpu_method("_PSS")
+ for pss, cpupaths in uniques.iteritems():
+ if not testsuite.test("_PSS must exist", pss is not None):
+ testsuite.print_detail(acpi.factor_commonprefix(cpupaths))
+ testsuite.print_detail('No _PSS exists')
+ continue
+
+ for n, pstate in enumerate(pss.pstates):
+ for cpupath in cpupaths:
+ apicid = cpupath_apicid(cpupath)
+ if apicid is None:
+ print 'Failed to find apicid for cpupath {}'.format(cpupath)
+ continue
+ bits.wrmsr(apicid, IA32_PERF_CTL, pstate.control)
+
+ # Detecting Turbo frequency requires at least 2 pstates
+ # since turbo frequency = max non-turbo frequency + 1
+ turbo = False
+ if len(pss.pstates) >= 2:
+ turbo = (n == 0 and pstate.core_frequency == (pss.pstates[1].core_frequency + 1))
+ if turbo:
+ # Needs to busywait, not sleep
+ start = time.time()
+ while (time.time() - start < 2):
+ pass
+
+ for duration in (0.1, 1.0):
+ frequency_data = bits.cpu_frequency(duration)
+ # Abort the test if no cpu frequency is not available
+ if frequency_data is None:
+ continue
+ aperf = frequency_data[1]
+ aperf = testutil.adjust_to_nearest(aperf, bclk/2)
+ aperf = int(aperf / 1000000)
+ if turbo:
+ if aperf >= pstate.core_frequency:
+ break
+ else:
+ if aperf == pstate.core_frequency:
+ break
+
+ if turbo:
+ testsuite.test("P{}: Turbo measured frequency {} >= expected {} MHz".format(n, aperf, pstate.core_frequency), aperf >= pstate.core_frequency)
+ else:
+ testsuite.test("P{}: measured frequency {} MHz == expected {} MHz".format(n, aperf, pstate.core_frequency), aperf == pstate.core_frequency)
+
+def test_psd_thread_scope():
+ uniques = acpi.parse_cpu_method("_PSD")
+ if not testsuite.test("_PSD (P-State Dependency) must exist for each processor", None not in uniques):
+ testsuite.print_detail(acpi.factor_commonprefix(uniques[None]))
+ testsuite.print_detail('No _PSD exists')
+ return
+ unique_num_dependencies = {}
+ unique_num_entries = {}
+ unique_revision = {}
+ unique_domain = {}
+ unique_coordination_type = {}
+ unique_num_processors = {}
+ for value, cpupaths in uniques.iteritems():
+ unique_num_dependencies.setdefault(len(value.dependencies), []).extend(cpupaths)
+ unique_num_entries.setdefault(value.dependencies[0].num_entries, []).extend(cpupaths)
+ unique_revision.setdefault(value.dependencies[0].revision, []).extend(cpupaths)
+ unique_domain.setdefault(value.dependencies[0].domain, []).extend(cpupaths)
+ unique_coordination_type.setdefault(value.dependencies[0].coordination_type, []).extend(cpupaths)
+ unique_num_processors.setdefault(value.dependencies[0].num_processors, []).extend(cpupaths)
+ def detail(d, fmt):
+ for value, cpupaths in sorted(d.iteritems(), key=(lambda (k,v): v)):
+ testsuite.print_detail(acpi.factor_commonprefix(cpupaths))
+ testsuite.print_detail(fmt.format(value))
+
+ testsuite.test('Dependency count for each processor must be 1', unique_num_dependencies.keys() == [1])
+ detail(unique_num_dependencies, 'Dependency count for each processor = {} (Expected 1)')
+ testsuite.test('_PSD.num_entries must be 5', unique_num_entries.keys() == [5])
+ detail(unique_num_entries, 'num_entries = {} (Expected 5)')
+ testsuite.test('_PSD.revision must be 0', unique_revision.keys() == [0])
+ detail(unique_revision, 'revision = {}')
+ testsuite.test('_PSD.coordination_type must be 0xFE (HW_ALL)', unique_coordination_type.keys() == [0xfe])
+ detail(unique_coordination_type, 'coordination_type = {:#x} (Expected 0xFE HW_ALL)')
+ testsuite.test('_PSD.domain must be unique (thread-scoped) for each processor', len(unique_domain) == len(acpi.get_cpupaths()))
+ detail(unique_domain, 'domain = {:#x} (Expected a unique value for each processor)')
+ testsuite.test('_PSD.num_processors must be 1', unique_num_processors.keys() == [1])
+ detail(unique_num_processors, 'num_processors = {} (Expected 1)')
+
+def test_table_checksum(data):
+ csum = sum(ord(c) for c in data) % 0x100
+ testsuite.test('ACPI table cumulative checksum must equal 0', csum == 0)
+ testsuite.print_detail("Cumulative checksum = {} (Expected 0)".format(csum))
+
+def test_apic():
+ data = acpi.get_table("APIC")
+ if data is None:
+ return
+ test_table_checksum(data)
+ apic = acpi.parse_apic()
+
+def test_dsdt():
+ data = acpi.get_table("DSDT")
+ if data is None:
+ return
+ test_table_checksum(data)
+
+def test_facp():
+ data = acpi.get_table("FACP")
+ if data is None:
+ return
+ test_table_checksum(data)
+ facp = acpi.parse_facp()
+
+def test_hpet():
+ data = acpi.get_table("HPET")
+ if data is None:
+ return
+ test_table_checksum(data)
+ hpet = acpi.parse_hpet()
+
+def test_mpst():
+ data = acpi.get_table("MPST")
+ if data is None:
+ return
+ test_table_checksum(data)
+ mpst = acpi.MPST(data)
+
+def test_rsdp():
+ data = acpi.get_table("RSD PTR ")
+ if data is None:
+ return
+
+ # Checksum the first 20 bytes per ACPI 1.0
+ csum = sum(ord(c) for c in data[:20]) % 0x100
+ testsuite.test('ACPI 1.0 table first 20 bytes cumulative checksum must equal 0', csum == 0)
+ testsuite.print_detail("Cumulative checksum = {} (Expected 0)".format(csum))
+
+ test_table_checksum(data)
+ rsdp = acpi.parse_rsdp()
+
+def test_xsdt():
+ data = acpi.get_table("XSDT")
+ if data is None:
+ return
+ test_table_checksum(data)
+ xsdt = acpi.parse_xsdt()
diff --git a/tests/avocado/acpi-bits/bits-tests/testcpuid.py2 b/tests/avocado/acpi-bits/bits-tests/testcpuid.py2
new file mode 100644
index 0000000000..7adefbe355
--- /dev/null
+++ b/tests/avocado/acpi-bits/bits-tests/testcpuid.py2
@@ -0,0 +1,87 @@
+# Copyright (c) 2012, Intel Corporation
+# All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of Intel Corporation nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# This script runs only from the biosbits VM.
+
+"""Tests and helpers for CPUID."""
+
+import bits
+import testsuite
+import testutil
+
+def cpuid_helper(function, index=None, shift=0, mask=~0, eax_mask=~0, ebx_mask=~0, ecx_mask=~0, edx_mask=~0):
+ if index is None:
+ index = 0
+ indexdesc = ""
+ else:
+ indexdesc = " index {0:#x}".format(index)
+
+ def find_mask(m):
+ if m == ~0:
+ return mask
+ return m
+ masks = map(find_mask, [eax_mask, ebx_mask, ecx_mask, edx_mask])
+
+ uniques = {}
+ for cpu in bits.cpus():
+ regs = bits.cpuid_result(*[(r >> shift) & m for r, m in zip(bits.cpuid(cpu, function, index), masks)])
+ uniques.setdefault(regs, []).append(cpu)
+
+ desc = ["CPUID function {:#x}{}".format(function, indexdesc)]
+
+ if shift != 0:
+ desc.append("Register values have been shifted by {}".format(shift))
+ if mask != ~0 or eax_mask != ~0 or ebx_mask != ~0 or ecx_mask != ~0 or edx_mask != ~0:
+ desc.append("Register values have been masked:")
+ shifted_masks = bits.cpuid_result(*[m << shift for m in masks])
+ desc.append("Masks: eax={eax:#010x} ebx={ebx:#010x} ecx={ecx:#010x} edx={edx:#010x}".format(**shifted_masks._asdict()))
+
+ if len(uniques) > 1:
+ regvalues = zip(*uniques.iterkeys())
+ common_masks = bits.cpuid_result(*map(testutil.find_common_mask, regvalues))
+ common_values = bits.cpuid_result(*[v[0] & m for v, m in zip(regvalues, common_masks)])
+ desc.append('Register values are not unique across all logical processors')
+ desc.append("Common bits: eax={eax:#010x} ebx={ebx:#010x} ecx={ecx:#010x} edx={edx:#010x}".format(**common_values._asdict()))
+ desc.append("Mask of common bits: {eax:#010x} {ebx:#010x} {ecx:#010x} {edx:#010x}".format(**common_masks._asdict()))
+
+ for regs in sorted(uniques.iterkeys()):
+ cpus = uniques[regs]
+ desc.append("Register value: eax={eax:#010x} ebx={ebx:#010x} ecx={ecx:#010x} edx={edx:#010x}".format(**regs._asdict()))
+ desc.append("On {0} CPUs: {1}".format(len(cpus), testutil.apicid_list(cpus)))
+
+ return uniques, desc
+
+def test_cpuid_consistency(text, function, index=None, shift=0, mask=~0, eax_mask=~0, ebx_mask=~0, ecx_mask=~0, edx_mask=~0):
+ uniques, desc = cpuid_helper(function, index, shift, mask, eax_mask, ebx_mask, ecx_mask, edx_mask)
+ desc[0] += " Consistency Check"
+ if text:
+ desc.insert(0, text)
+ status = testsuite.test(desc[0], len(uniques) == 1)
+ for line in desc[1:]:
+ testsuite.print_detail(line)
+ return status
diff --git a/tests/avocado/avocado_qemu/__init__.py b/tests/avocado/avocado_qemu/__init__.py
new file mode 100644
index 0000000000..304c428168
--- /dev/null
+++ b/tests/avocado/avocado_qemu/__init__.py
@@ -0,0 +1,681 @@
+# Test class and utilities for functional tests
+#
+# Copyright (c) 2018 Red Hat, Inc.
+#
+# Author:
+# Cleber Rosa <crosa@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import logging
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+import time
+import uuid
+
+import avocado
+from avocado.utils import cloudinit, datadrainer, process, ssh, vmimage
+from avocado.utils.path import find_command
+
+from qemu.machine import QEMUMachine
+from qemu.utils import (get_info_usernet_hostfwd_port, kvm_available,
+ tcg_available)
+
+
+#: The QEMU build root directory. It may also be the source directory
+#: if building from the source dir, but it's safer to use BUILD_DIR for
+#: that purpose. Be aware that if this code is moved outside of a source
+#: and build tree, it will not be accurate.
+BUILD_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
+
+if os.path.islink(os.path.dirname(os.path.dirname(__file__))):
+ # The link to the avocado tests dir in the source code directory
+ lnk = os.path.dirname(os.path.dirname(__file__))
+ #: The QEMU root source directory
+ SOURCE_DIR = os.path.dirname(os.path.dirname(os.readlink(lnk)))
+else:
+ SOURCE_DIR = BUILD_DIR
+
+
+def has_cmd(name, args=None):
+ """
+ This function is for use in a @avocado.skipUnless decorator, e.g.:
+
+ @skipUnless(*has_cmd('sudo -n', ('sudo', '-n', 'true')))
+ def test_something_that_needs_sudo(self):
+ ...
+ """
+
+ if args is None:
+ args = ('which', name)
+
+ try:
+ _, stderr, exitcode = run_cmd(args)
+ except Exception as e:
+ exitcode = -1
+ stderr = str(e)
+
+ if exitcode != 0:
+ cmd_line = ' '.join(args)
+ err = f'{name} required, but "{cmd_line}" failed: {stderr.strip()}'
+ return (False, err)
+ else:
+ return (True, '')
+
+def has_cmds(*cmds):
+ """
+ This function is for use in a @avocado.skipUnless decorator and
+ allows checking for the availability of multiple commands, e.g.:
+
+ @skipUnless(*has_cmds(('cmd1', ('cmd1', '--some-parameter')),
+ 'cmd2', 'cmd3'))
+ def test_something_that_needs_cmd1_and_cmd2(self):
+ ...
+ """
+
+ for cmd in cmds:
+ if isinstance(cmd, str):
+ cmd = (cmd,)
+
+ ok, errstr = has_cmd(*cmd)
+ if not ok:
+ return (False, errstr)
+
+ return (True, '')
+
+def run_cmd(args):
+ subp = subprocess.Popen(args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ universal_newlines=True)
+ stdout, stderr = subp.communicate()
+ ret = subp.returncode
+
+ return (stdout, stderr, ret)
+
+def is_readable_executable_file(path):
+ return os.path.isfile(path) and os.access(path, os.R_OK | os.X_OK)
+
+
+def pick_default_qemu_bin(bin_prefix='qemu-system-', arch=None):
+ """
+ Picks the path of a QEMU binary, starting either in the current working
+ directory or in the source tree root directory.
+
+ :param arch: the arch to use when looking for a QEMU binary (the target
+ will match the arch given). If None (the default), arch
+ will be the current host system arch (as given by
+ :func:`os.uname`).
+ :type arch: str
+ :returns: the path to the default QEMU binary or None if one could not
+ be found
+ :rtype: str or None
+ """
+ if arch is None:
+ arch = os.uname()[4]
+ # qemu binary path does not match arch for powerpc, handle it
+ if 'ppc64le' in arch:
+ arch = 'ppc64'
+ qemu_bin_name = bin_prefix + arch
+ qemu_bin_paths = [
+ os.path.join(".", qemu_bin_name),
+ os.path.join(BUILD_DIR, qemu_bin_name),
+ os.path.join(BUILD_DIR, "build", qemu_bin_name),
+ ]
+ for path in qemu_bin_paths:
+ if is_readable_executable_file(path):
+ return path
+ return None
+
+
+def _console_interaction(test, success_message, failure_message,
+ send_string, keep_sending=False, vm=None):
+ assert not keep_sending or send_string
+ if vm is None:
+ vm = test.vm
+ console = vm.console_file
+ console_logger = logging.getLogger('console')
+ while True:
+ if send_string:
+ vm.console_socket.sendall(send_string.encode())
+ if not keep_sending:
+ send_string = None # send only once
+ try:
+ msg = console.readline().decode().strip()
+ except UnicodeDecodeError:
+ msg = None
+ if not msg:
+ continue
+ console_logger.debug(msg)
+ if success_message is None or success_message in msg:
+ break
+ if failure_message and failure_message in msg:
+ console.close()
+ fail = 'Failure message found in console: "%s". Expected: "%s"' % \
+ (failure_message, success_message)
+ test.fail(fail)
+
+def interrupt_interactive_console_until_pattern(test, success_message,
+ failure_message=None,
+ interrupt_string='\r'):
+ """
+ Keep sending a string to interrupt a console prompt, while logging the
+ console output. Typical use case is to break a boot loader prompt, such:
+
+ Press a key within 5 seconds to interrupt boot process.
+ 5
+ 4
+ 3
+ 2
+ 1
+ Booting default image...
+
+ :param test: an Avocado test containing a VM that will have its console
+ read and probed for a success or failure message
+ :type test: :class:`avocado_qemu.QemuSystemTest`
+ :param success_message: if this message appears, test succeeds
+ :param failure_message: if this message appears, test fails
+ :param interrupt_string: a string to send to the console before trying
+ to read a new line
+ """
+ _console_interaction(test, success_message, failure_message,
+ interrupt_string, True)
+
+def wait_for_console_pattern(test, success_message, failure_message=None,
+ vm=None):
+ """
+ Waits for messages to appear on the console, while logging the content
+
+ :param test: an Avocado test containing a VM that will have its console
+ read and probed for a success or failure message
+ :type test: :class:`avocado_qemu.QemuSystemTest`
+ :param success_message: if this message appears, test succeeds
+ :param failure_message: if this message appears, test fails
+ """
+ _console_interaction(test, success_message, failure_message, None, vm=vm)
+
+def exec_command(test, command):
+ """
+ Send a command to a console (appending CRLF characters), while logging
+ the content.
+
+ :param test: an Avocado test containing a VM.
+ :type test: :class:`avocado_qemu.QemuSystemTest`
+ :param command: the command to send
+ :type command: str
+ """
+ _console_interaction(test, None, None, command + '\r')
+
+def exec_command_and_wait_for_pattern(test, command,
+ success_message, failure_message=None):
+ """
+ Send a command to a console (appending CRLF characters), then wait
+ for success_message to appear on the console, while logging the.
+ content. Mark the test as failed if failure_message is found instead.
+
+ :param test: an Avocado test containing a VM that will have its console
+ read and probed for a success or failure message
+ :type test: :class:`avocado_qemu.QemuSystemTest`
+ :param command: the command to send
+ :param success_message: if this message appears, test succeeds
+ :param failure_message: if this message appears, test fails
+ """
+ _console_interaction(test, success_message, failure_message, command + '\r')
+
+class QemuBaseTest(avocado.Test):
+
+ # default timeout for all tests, can be overridden
+ timeout = 120
+
+ def _get_unique_tag_val(self, tag_name):
+ """
+ Gets a tag value, if unique for a key
+ """
+ vals = self.tags.get(tag_name, [])
+ if len(vals) == 1:
+ return vals.pop()
+ return None
+
+ def setUp(self, bin_prefix):
+ self.arch = self.params.get('arch',
+ default=self._get_unique_tag_val('arch'))
+
+ self.cpu = self.params.get('cpu',
+ default=self._get_unique_tag_val('cpu'))
+
+ default_qemu_bin = pick_default_qemu_bin(bin_prefix, arch=self.arch)
+ self.qemu_bin = self.params.get('qemu_bin',
+ default=default_qemu_bin)
+ if self.qemu_bin is None:
+ self.cancel("No QEMU binary defined or found in the build tree")
+
+ def fetch_asset(self, name,
+ asset_hash, algorithm=None,
+ locations=None, expire=None,
+ find_only=False, cancel_on_missing=True):
+ return super().fetch_asset(name,
+ asset_hash=asset_hash,
+ algorithm=algorithm,
+ locations=locations,
+ expire=expire,
+ find_only=find_only,
+ cancel_on_missing=cancel_on_missing)
+
+
+class QemuSystemTest(QemuBaseTest):
+ """Facilitates system emulation tests."""
+
+ def setUp(self):
+ self._vms = {}
+
+ super().setUp('qemu-system-')
+
+ accel_required = self._get_unique_tag_val('accel')
+ if accel_required:
+ self.require_accelerator(accel_required)
+
+ self.machine = self.params.get('machine',
+ default=self._get_unique_tag_val('machine'))
+
+ def require_accelerator(self, accelerator):
+ """
+ Requires an accelerator to be available for the test to continue
+
+ It takes into account the currently set qemu binary.
+
+ If the check fails, the test is canceled. If the check itself
+ for the given accelerator is not available, the test is also
+ canceled.
+
+ :param accelerator: name of the accelerator, such as "kvm" or "tcg"
+ :type accelerator: str
+ """
+ checker = {'tcg': tcg_available,
+ 'kvm': kvm_available}.get(accelerator)
+ if checker is None:
+ self.cancel("Don't know how to check for the presence "
+ "of accelerator %s" % accelerator)
+ if not checker(qemu_bin=self.qemu_bin):
+ self.cancel("%s accelerator does not seem to be "
+ "available" % accelerator)
+
+ def require_netdev(self, netdevname):
+ netdevhelp = run_cmd([self.qemu_bin,
+ '-M', 'none', '-netdev', 'help'])[0];
+ if netdevhelp.find('\n' + netdevname + '\n') < 0:
+ self.cancel('no support for user networking')
+
+ def require_multiprocess(self):
+ """
+ Test for the presence of the x-pci-proxy-dev which is required
+ to support multiprocess.
+ """
+ devhelp = run_cmd([self.qemu_bin,
+ '-M', 'none', '-device', 'help'])[0];
+ if devhelp.find('x-pci-proxy-dev') < 0:
+ self.cancel('no support for multiprocess device emulation')
+
+ def _new_vm(self, name, *args):
+ self._sd = tempfile.TemporaryDirectory(prefix="qemu_")
+ vm = QEMUMachine(self.qemu_bin, base_temp_dir=self.workdir,
+ log_dir=self.logdir)
+ self.log.debug('QEMUMachine "%s" created', name)
+ self.log.debug('QEMUMachine "%s" temp_dir: %s', name, vm.temp_dir)
+ self.log.debug('QEMUMachine "%s" log_dir: %s', name, vm.log_dir)
+ if args:
+ vm.add_args(*args)
+ return vm
+
+ def get_qemu_img(self):
+ self.log.debug('Looking for and selecting a qemu-img binary')
+
+ # If qemu-img has been built, use it, otherwise the system wide one
+ # will be used.
+ qemu_img = os.path.join(BUILD_DIR, 'qemu-img')
+ if not os.path.exists(qemu_img):
+ qemu_img = find_command('qemu-img', False)
+ if qemu_img is False:
+ self.cancel('Could not find "qemu-img"')
+
+ return qemu_img
+
+ @property
+ def vm(self):
+ return self.get_vm(name='default')
+
+ def get_vm(self, *args, name=None):
+ if not name:
+ name = str(uuid.uuid4())
+ if self._vms.get(name) is None:
+ self._vms[name] = self._new_vm(name, *args)
+ if self.cpu is not None:
+ self._vms[name].add_args('-cpu', self.cpu)
+ if self.machine is not None:
+ self._vms[name].set_machine(self.machine)
+ return self._vms[name]
+
+ def set_vm_arg(self, arg, value):
+ """
+ Set an argument to list of extra arguments to be given to the QEMU
+ binary. If the argument already exists then its value is replaced.
+
+ :param arg: the QEMU argument, such as "-cpu" in "-cpu host"
+ :type arg: str
+ :param value: the argument value, such as "host" in "-cpu host"
+ :type value: str
+ """
+ if not arg or not value:
+ return
+ if arg not in self.vm.args:
+ self.vm.args.extend([arg, value])
+ else:
+ idx = self.vm.args.index(arg) + 1
+ if idx < len(self.vm.args):
+ self.vm.args[idx] = value
+ else:
+ self.vm.args.append(value)
+
+ def tearDown(self):
+ for vm in self._vms.values():
+ vm.shutdown()
+ self._sd = None
+ super().tearDown()
+
+
+class QemuUserTest(QemuBaseTest):
+ """Facilitates user-mode emulation tests."""
+
+ def setUp(self):
+ self._ldpath = []
+ super().setUp('qemu-')
+
+ def add_ldpath(self, ldpath):
+ self._ldpath.append(os.path.abspath(ldpath))
+
+ def run(self, bin_path, args=[]):
+ qemu_args = " ".join(["-L %s" % ldpath for ldpath in self._ldpath])
+ bin_args = " ".join(args)
+ return process.run("%s %s %s %s" % (self.qemu_bin, qemu_args,
+ bin_path, bin_args))
+
+
+class LinuxSSHMixIn:
+ """Contains utility methods for interacting with a guest via SSH."""
+
+ def ssh_connect(self, username, credential, credential_is_key=True):
+ self.ssh_logger = logging.getLogger('ssh')
+ res = self.vm.cmd('human-monitor-command',
+ command_line='info usernet')
+ port = get_info_usernet_hostfwd_port(res)
+ self.assertIsNotNone(port)
+ self.assertGreater(port, 0)
+ self.log.debug('sshd listening on port: %d', port)
+ if credential_is_key:
+ self.ssh_session = ssh.Session('127.0.0.1', port=port,
+ user=username, key=credential)
+ else:
+ self.ssh_session = ssh.Session('127.0.0.1', port=port,
+ user=username, password=credential)
+ for i in range(10):
+ try:
+ self.ssh_session.connect()
+ return
+ except:
+ time.sleep(i)
+ self.fail('ssh connection timeout')
+
+ def ssh_command(self, command):
+ self.ssh_logger.info(command)
+ result = self.ssh_session.cmd(command)
+ stdout_lines = [line.rstrip() for line
+ in result.stdout_text.splitlines()]
+ for line in stdout_lines:
+ self.ssh_logger.info(line)
+ stderr_lines = [line.rstrip() for line
+ in result.stderr_text.splitlines()]
+ for line in stderr_lines:
+ self.ssh_logger.warning(line)
+
+ self.assertEqual(result.exit_status, 0,
+ f'Guest command failed: {command}')
+ return stdout_lines, stderr_lines
+
+ def ssh_command_output_contains(self, cmd, exp):
+ stdout, _ = self.ssh_command(cmd)
+ for line in stdout:
+ if exp in line:
+ break
+ else:
+ self.fail('"%s" output does not contain "%s"' % (cmd, exp))
+
+class LinuxDistro:
+ """Represents a Linux distribution
+
+ Holds information of known distros.
+ """
+ #: A collection of known distros and their respective image checksum
+ KNOWN_DISTROS = {
+ 'fedora': {
+ '31': {
+ 'x86_64':
+ {'checksum': ('e3c1b309d9203604922d6e255c2c5d09'
+ '8a309c2d46215d8fc026954f3c5c27a0'),
+ 'pxeboot_url': ('https://archives.fedoraproject.org/'
+ 'pub/archive/fedora/linux/releases/31/'
+ 'Everything/x86_64/os/images/pxeboot/'),
+ 'kernel_params': ('root=UUID=b1438b9b-2cab-4065-a99a-'
+ '08a96687f73c ro no_timer_check '
+ 'net.ifnames=0 console=tty1 '
+ 'console=ttyS0,115200n8'),
+ },
+ 'aarch64':
+ {'checksum': ('1e18d9c0cf734940c4b5d5ec592facae'
+ 'd2af0ad0329383d5639c997fdf16fe49'),
+ 'pxeboot_url': 'https://archives.fedoraproject.org/'
+ 'pub/archive/fedora/linux/releases/31/'
+ 'Everything/aarch64/os/images/pxeboot/',
+ 'kernel_params': ('root=UUID=b6950a44-9f3c-4076-a9c2-'
+ '355e8475b0a7 ro earlyprintk=pl011,0x9000000'
+ ' ignore_loglevel no_timer_check'
+ ' printk.time=1 rd_NO_PLYMOUTH'
+ ' console=ttyAMA0'),
+ },
+ 'ppc64':
+ {'checksum': ('7c3528b85a3df4b2306e892199a9e1e4'
+ '3f991c506f2cc390dc4efa2026ad2f58')},
+ 's390x':
+ {'checksum': ('4caaab5a434fd4d1079149a072fdc789'
+ '1e354f834d355069ca982fdcaf5a122d')},
+ },
+ '32': {
+ 'aarch64':
+ {'checksum': ('b367755c664a2d7a26955bbfff985855'
+ 'adfa2ca15e908baf15b4b176d68d3967'),
+ 'pxeboot_url': ('http://dl.fedoraproject.org/pub/fedora/linux/'
+ 'releases/32/Server/aarch64/os/images/'
+ 'pxeboot/'),
+ 'kernel_params': ('root=UUID=3df75b65-be8d-4db4-8655-'
+ '14d95c0e90c5 ro no_timer_check net.ifnames=0'
+ ' console=tty1 console=ttyS0,115200n8'),
+ },
+ },
+ '33': {
+ 'aarch64':
+ {'checksum': ('e7f75cdfd523fe5ac2ca9eeece68edc1'
+ 'a81f386a17f969c1d1c7c87031008a6b'),
+ 'pxeboot_url': ('http://dl.fedoraproject.org/pub/fedora/linux/'
+ 'releases/33/Server/aarch64/os/images/'
+ 'pxeboot/'),
+ 'kernel_params': ('root=UUID=d20b3ffa-6397-4a63-a734-'
+ '1126a0208f8a ro no_timer_check net.ifnames=0'
+ ' console=tty1 console=ttyS0,115200n8'
+ ' console=tty0'),
+ },
+ },
+ }
+ }
+
+ def __init__(self, name, version, arch):
+ self.name = name
+ self.version = version
+ self.arch = arch
+ try:
+ info = self.KNOWN_DISTROS.get(name).get(version).get(arch)
+ except AttributeError:
+ # Unknown distro
+ info = None
+ self._info = info or {}
+
+ @property
+ def checksum(self):
+ """Gets the cloud-image file checksum"""
+ return self._info.get('checksum', None)
+
+ @checksum.setter
+ def checksum(self, value):
+ self._info['checksum'] = value
+
+ @property
+ def pxeboot_url(self):
+ """Gets the repository url where pxeboot files can be found"""
+ return self._info.get('pxeboot_url', None)
+
+ @property
+ def default_kernel_params(self):
+ """Gets the default kernel parameters"""
+ return self._info.get('kernel_params', None)
+
+
+class LinuxTest(LinuxSSHMixIn, QemuSystemTest):
+ """Facilitates having a cloud-image Linux based available.
+
+ For tests that intend to interact with guests, this is a better choice
+ to start with than the more vanilla `QemuSystemTest` class.
+ """
+
+ distro = None
+ username = 'root'
+ password = 'password'
+ smp = '2'
+ memory = '1024'
+
+ def _set_distro(self):
+ distro_name = self.params.get(
+ 'distro',
+ default=self._get_unique_tag_val('distro'))
+ if not distro_name:
+ distro_name = 'fedora'
+
+ distro_version = self.params.get(
+ 'distro_version',
+ default=self._get_unique_tag_val('distro_version'))
+ if not distro_version:
+ distro_version = '31'
+
+ self.distro = LinuxDistro(distro_name, distro_version, self.arch)
+
+ # The distro checksum behaves differently than distro name and
+ # version. First, it does not respect a tag with the same
+ # name, given that it's not expected to be used for filtering
+ # (distro name versions are the natural choice). Second, the
+ # order of precedence is: parameter, attribute and then value
+ # from KNOWN_DISTROS.
+ distro_checksum = self.params.get('distro_checksum',
+ default=None)
+ if distro_checksum:
+ self.distro.checksum = distro_checksum
+
+ def setUp(self, ssh_pubkey=None, network_device_type='virtio-net'):
+ super().setUp()
+ self.require_netdev('user')
+ self._set_distro()
+ self.vm.add_args('-smp', self.smp)
+ self.vm.add_args('-m', self.memory)
+ # The following network device allows for SSH connections
+ self.vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22',
+ '-device', '%s,netdev=vnet' % network_device_type)
+ self.set_up_boot()
+ if ssh_pubkey is None:
+ ssh_pubkey, self.ssh_key = self.set_up_existing_ssh_keys()
+ self.set_up_cloudinit(ssh_pubkey)
+
+ def set_up_existing_ssh_keys(self):
+ ssh_public_key = os.path.join(SOURCE_DIR, 'tests', 'keys', 'id_rsa.pub')
+ source_private_key = os.path.join(SOURCE_DIR, 'tests', 'keys', 'id_rsa')
+ ssh_dir = os.path.join(self.workdir, '.ssh')
+ os.mkdir(ssh_dir, mode=0o700)
+ ssh_private_key = os.path.join(ssh_dir,
+ os.path.basename(source_private_key))
+ shutil.copyfile(source_private_key, ssh_private_key)
+ os.chmod(ssh_private_key, 0o600)
+ return (ssh_public_key, ssh_private_key)
+
+ def download_boot(self):
+ # Set the qemu-img binary.
+ # If none is available, the test will cancel.
+ vmimage.QEMU_IMG = super().get_qemu_img()
+
+ self.log.info('Downloading/preparing boot image')
+ # Fedora 31 only provides ppc64le images
+ image_arch = self.arch
+ if self.distro.name == 'fedora':
+ if image_arch == 'ppc64':
+ image_arch = 'ppc64le'
+
+ try:
+ boot = vmimage.get(
+ self.distro.name, arch=image_arch, version=self.distro.version,
+ checksum=self.distro.checksum,
+ algorithm='sha256',
+ cache_dir=self.cache_dirs[0],
+ snapshot_dir=self.workdir)
+ except:
+ self.cancel('Failed to download/prepare boot image')
+ return boot.path
+
+ def prepare_cloudinit(self, ssh_pubkey=None):
+ self.log.info('Preparing cloudinit image')
+ try:
+ cloudinit_iso = os.path.join(self.workdir, 'cloudinit.iso')
+ pubkey_content = None
+ if ssh_pubkey:
+ with open(ssh_pubkey) as pubkey:
+ pubkey_content = pubkey.read()
+ cloudinit.iso(cloudinit_iso, self.name,
+ username=self.username,
+ password=self.password,
+ # QEMU's hard coded usermode router address
+ phone_home_host='10.0.2.2',
+ phone_home_port=self.phone_server.server_port,
+ authorized_key=pubkey_content)
+ except Exception:
+ self.cancel('Failed to prepare the cloudinit image')
+ return cloudinit_iso
+
+ def set_up_boot(self):
+ path = self.download_boot()
+ self.vm.add_args('-drive', 'file=%s' % path)
+
+ def set_up_cloudinit(self, ssh_pubkey=None):
+ self.phone_server = cloudinit.PhoneHomeServer(('0.0.0.0', 0),
+ self.name)
+ cloudinit_iso = self.prepare_cloudinit(ssh_pubkey)
+ self.vm.add_args('-drive', 'file=%s,format=raw' % cloudinit_iso)
+
+ def launch_and_wait(self, set_up_ssh_connection=True):
+ self.vm.set_console()
+ self.vm.launch()
+ console_drainer = datadrainer.LineLogger(self.vm.console_socket.fileno(),
+ logger=self.log.getChild('console'))
+ console_drainer.start()
+ self.log.info('VM launched, waiting for boot confirmation from guest')
+ while not self.phone_server.instance_phoned_back:
+ self.phone_server.handle_request()
+
+ if set_up_ssh_connection:
+ self.log.info('Setting up the SSH connection')
+ self.ssh_connect(self.username, self.ssh_key)
diff --git a/tests/avocado/boot_linux.py b/tests/avocado/boot_linux.py
new file mode 100644
index 0000000000..cdce4cbcba
--- /dev/null
+++ b/tests/avocado/boot_linux.py
@@ -0,0 +1,131 @@
+# Functional test that boots a complete Linux system via a cloud image
+#
+# Copyright (c) 2018-2020 Red Hat, Inc.
+#
+# Author:
+# Cleber Rosa <crosa@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import os
+
+from avocado_qemu import LinuxTest, BUILD_DIR
+
+from avocado import skipUnless
+
+
+class BootLinuxX8664(LinuxTest):
+ """
+ :avocado: tags=arch:x86_64
+ """
+ timeout = 480
+
+ def test_pc_i440fx_tcg(self):
+ """
+ :avocado: tags=machine:pc
+ :avocado: tags=accel:tcg
+ """
+ self.require_accelerator("tcg")
+ self.vm.add_args("-accel", "tcg")
+ self.launch_and_wait(set_up_ssh_connection=False)
+
+ def test_pc_i440fx_kvm(self):
+ """
+ :avocado: tags=machine:pc
+ :avocado: tags=accel:kvm
+ """
+ self.require_accelerator("kvm")
+ self.vm.add_args("-accel", "kvm")
+ self.launch_and_wait(set_up_ssh_connection=False)
+
+ def test_pc_q35_tcg(self):
+ """
+ :avocado: tags=machine:q35
+ :avocado: tags=accel:tcg
+ """
+ self.require_accelerator("tcg")
+ self.vm.add_args("-accel", "tcg")
+ self.launch_and_wait(set_up_ssh_connection=False)
+
+ def test_pc_q35_kvm(self):
+ """
+ :avocado: tags=machine:q35
+ :avocado: tags=accel:kvm
+ """
+ self.require_accelerator("kvm")
+ self.vm.add_args("-accel", "kvm")
+ self.launch_and_wait(set_up_ssh_connection=False)
+
+
+# For Aarch64 we only boot KVM tests in CI as booting the current
+# Fedora OS in TCG tests is very heavyweight. There are lighter weight
+# distros which we use in the machine_aarch64_virt.py tests.
+class BootLinuxAarch64(LinuxTest):
+ """
+ :avocado: tags=arch:aarch64
+ :avocado: tags=machine:virt
+ """
+ timeout = 720
+
+ def test_virt_kvm(self):
+ """
+ :avocado: tags=accel:kvm
+ :avocado: tags=cpu:host
+ """
+ self.require_accelerator("kvm")
+ self.vm.add_args("-accel", "kvm")
+ self.vm.add_args("-machine", "virt,gic-version=host")
+ self.vm.add_args('-bios',
+ os.path.join(BUILD_DIR, 'pc-bios',
+ 'edk2-aarch64-code.fd'))
+ self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0')
+ self.vm.add_args('-object', 'rng-random,id=rng0,filename=/dev/urandom')
+ self.launch_and_wait(set_up_ssh_connection=False)
+
+
+# See the tux_baseline.py tests for almost the same coverage in a lot
+# less time.
+class BootLinuxPPC64(LinuxTest):
+ """
+ :avocado: tags=arch:ppc64
+ """
+
+ timeout = 360
+
+ @skipUnless(os.getenv('SPEED') == 'slow', 'runtime limited')
+ def test_pseries_tcg(self):
+ """
+ :avocado: tags=machine:pseries
+ :avocado: tags=accel:tcg
+ """
+ self.require_accelerator("tcg")
+ self.vm.add_args("-accel", "tcg")
+ self.launch_and_wait(set_up_ssh_connection=False)
+
+ def test_pseries_kvm(self):
+ """
+ :avocado: tags=machine:pseries
+ :avocado: tags=accel:kvm
+ """
+ self.require_accelerator("kvm")
+ self.vm.add_args("-accel", "kvm")
+ self.vm.add_args("-machine", "cap-ccf-assist=off")
+ self.launch_and_wait(set_up_ssh_connection=False)
+
+class BootLinuxS390X(LinuxTest):
+ """
+ :avocado: tags=arch:s390x
+ """
+
+ timeout = 240
+
+ @skipUnless(os.getenv('SPEED') == 'slow', 'runtime limited')
+ def test_s390_ccw_virtio_tcg(self):
+ """
+ :avocado: tags=machine:s390-ccw-virtio
+ :avocado: tags=accel:tcg
+ """
+ self.require_accelerator("tcg")
+ self.vm.add_args("-accel", "tcg")
+ self.launch_and_wait(set_up_ssh_connection=False)
diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py
new file mode 100644
index 0000000000..180ac17326
--- /dev/null
+++ b/tests/avocado/boot_linux_console.py
@@ -0,0 +1,1547 @@
+# Functional test that boots a Linux kernel and checks the console
+#
+# Copyright (c) 2018 Red Hat, Inc.
+#
+# Author:
+# Cleber Rosa <crosa@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import os
+import lzma
+import gzip
+import shutil
+
+from avocado import skip
+from avocado import skipUnless
+from avocado import skipUnless
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import exec_command
+from avocado_qemu import exec_command_and_wait_for_pattern
+from avocado_qemu import interrupt_interactive_console_until_pattern
+from avocado_qemu import wait_for_console_pattern
+from avocado.utils import process
+from avocado.utils import archive
+
+"""
+Round up to next power of 2
+"""
+def pow2ceil(x):
+ return 1 if x == 0 else 2**(x - 1).bit_length()
+
+def file_truncate(path, size):
+ if size != os.path.getsize(path):
+ with open(path, 'ab+') as fd:
+ fd.truncate(size)
+
+"""
+Expand file size to next power of 2
+"""
+def image_pow2ceil_expand(path):
+ size = os.path.getsize(path)
+ size_aligned = pow2ceil(size)
+ if size != size_aligned:
+ with open(path, 'ab+') as fd:
+ fd.truncate(size_aligned)
+
+class LinuxKernelTest(QemuSystemTest):
+ KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
+
+ def wait_for_console_pattern(self, success_message, vm=None):
+ wait_for_console_pattern(self, success_message,
+ failure_message='Kernel panic - not syncing',
+ vm=vm)
+
+ def extract_from_deb(self, deb, path):
+ """
+ Extracts a file from a deb package into the test workdir
+
+ :param deb: path to the deb archive
+ :param path: path within the deb archive of the file to be extracted
+ :returns: path of the extracted file
+ """
+ cwd = os.getcwd()
+ os.chdir(self.workdir)
+ file_path = process.run("ar t %s" % deb).stdout_text.split()[2]
+ process.run("ar x %s %s" % (deb, file_path))
+ archive.extract(file_path, self.workdir)
+ os.chdir(cwd)
+ # Return complete path to extracted file. Because callers to
+ # extract_from_deb() specify 'path' with a leading slash, it is
+ # necessary to use os.path.relpath() as otherwise os.path.join()
+ # interprets it as an absolute path and drops the self.workdir part.
+ return os.path.normpath(os.path.join(self.workdir,
+ os.path.relpath(path, '/')))
+
+ def extract_from_rpm(self, rpm, path):
+ """
+ Extracts a file from an RPM package into the test workdir.
+
+ :param rpm: path to the rpm archive
+ :param path: path within the rpm archive of the file to be extracted
+ needs to be a relative path (starting with './') because
+ cpio(1), which is used to extract the file, expects that.
+ :returns: path of the extracted file
+ """
+ cwd = os.getcwd()
+ os.chdir(self.workdir)
+ process.run("rpm2cpio %s | cpio -id %s" % (rpm, path), shell=True)
+ os.chdir(cwd)
+ return os.path.normpath(os.path.join(self.workdir, path))
+
+class BootLinuxConsole(LinuxKernelTest):
+ """
+ Boots a Linux kernel and checks that the console is operational and the
+ kernel command line is properly passed from QEMU to the kernel
+ """
+ timeout = 90
+
+ def test_x86_64_pc(self):
+ """
+ :avocado: tags=arch:x86_64
+ :avocado: tags=machine:pc
+ """
+ kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora'
+ '/linux/releases/29/Everything/x86_64/os/images/pxeboot'
+ '/vmlinuz')
+ kernel_hash = '23bebd2680757891cf7adedb033532163a792495'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ self.vm.set_console()
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0'
+ self.vm.add_args('-kernel', kernel_path,
+ '-append', kernel_command_line)
+ self.vm.launch()
+ console_pattern = 'Kernel command line: %s' % kernel_command_line
+ self.wait_for_console_pattern(console_pattern)
+
+ def test_mips_malta(self):
+ """
+ :avocado: tags=arch:mips
+ :avocado: tags=machine:malta
+ :avocado: tags=endian:big
+ """
+ deb_url = ('http://snapshot.debian.org/archive/debian/'
+ '20130217T032700Z/pool/main/l/linux-2.6/'
+ 'linux-image-2.6.32-5-4kc-malta_2.6.32-48_mips.deb')
+ deb_hash = 'a8cfc28ad8f45f54811fc6cf74fc43ffcfe0ba04'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path,
+ '/boot/vmlinux-2.6.32-5-4kc-malta')
+
+ self.vm.set_console()
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0'
+ self.vm.add_args('-kernel', kernel_path,
+ '-append', kernel_command_line)
+ self.vm.launch()
+ console_pattern = 'Kernel command line: %s' % kernel_command_line
+ self.wait_for_console_pattern(console_pattern)
+
+ def test_mips64el_malta(self):
+ """
+ This test requires the ar tool to extract "data.tar.gz" from
+ the Debian package.
+
+ The kernel can be rebuilt using this Debian kernel source [1] and
+ following the instructions on [2].
+
+ [1] http://snapshot.debian.org/package/linux-2.6/2.6.32-48/
+ #linux-source-2.6.32_2.6.32-48
+ [2] https://kernel-team.pages.debian.net/kernel-handbook/
+ ch-common-tasks.html#s-common-official
+
+ :avocado: tags=arch:mips64el
+ :avocado: tags=machine:malta
+ """
+ deb_url = ('http://snapshot.debian.org/archive/debian/'
+ '20130217T032700Z/pool/main/l/linux-2.6/'
+ 'linux-image-2.6.32-5-5kc-malta_2.6.32-48_mipsel.deb')
+ deb_hash = '1aaec92083bf22fda31e0d27fa8d9a388e5fc3d5'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path,
+ '/boot/vmlinux-2.6.32-5-5kc-malta')
+
+ self.vm.set_console()
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0'
+ self.vm.add_args('-kernel', kernel_path,
+ '-append', kernel_command_line)
+ self.vm.launch()
+ console_pattern = 'Kernel command line: %s' % kernel_command_line
+ self.wait_for_console_pattern(console_pattern)
+
+ def test_mips64el_fuloong2e(self):
+ """
+ :avocado: tags=arch:mips64el
+ :avocado: tags=machine:fuloong2e
+ :avocado: tags=endian:little
+ """
+ deb_url = ('http://archive.debian.org/debian/pool/main/l/linux/'
+ 'linux-image-3.16.0-6-loongson-2e_3.16.56-1+deb8u1_mipsel.deb')
+ deb_hash = 'd04d446045deecf7b755ef576551de0c4184dd44'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path,
+ '/boot/vmlinux-3.16.0-6-loongson-2e')
+
+ self.vm.set_console()
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0'
+ self.vm.add_args('-kernel', kernel_path,
+ '-append', kernel_command_line)
+ self.vm.launch()
+ console_pattern = 'Kernel command line: %s' % kernel_command_line
+ self.wait_for_console_pattern(console_pattern)
+
+ def test_mips_malta_cpio(self):
+ """
+ :avocado: tags=arch:mips
+ :avocado: tags=machine:malta
+ :avocado: tags=endian:big
+ """
+ deb_url = ('http://snapshot.debian.org/archive/debian/'
+ '20160601T041800Z/pool/main/l/linux/'
+ 'linux-image-4.5.0-2-4kc-malta_4.5.5-1_mips.deb')
+ deb_hash = 'a3c84f3e88b54e06107d65a410d1d1e8e0f340f8'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path,
+ '/boot/vmlinux-4.5.0-2-4kc-malta')
+ initrd_url = ('https://github.com/groeck/linux-build-test/raw/'
+ '8584a59ed9e5eb5ee7ca91f6d74bbb06619205b8/rootfs/'
+ 'mips/rootfs.cpio.gz')
+ initrd_hash = 'bf806e17009360a866bf537f6de66590de349a99'
+ initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
+ initrd_path = self.workdir + "rootfs.cpio"
+ archive.gzip_uncompress(initrd_path_gz, initrd_path)
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE
+ + 'console=ttyS0 console=tty '
+ + 'rdinit=/sbin/init noreboot')
+ self.vm.add_args('-kernel', kernel_path,
+ '-initrd', initrd_path,
+ '-append', kernel_command_line,
+ '-no-reboot')
+ self.vm.launch()
+ self.wait_for_console_pattern('Boot successful.')
+
+ exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo',
+ 'BogoMIPS')
+ exec_command_and_wait_for_pattern(self, 'uname -a',
+ 'Debian')
+ exec_command_and_wait_for_pattern(self, 'reboot',
+ 'reboot: Restarting system')
+ # Wait for VM to shut down gracefully
+ self.vm.wait()
+
+ @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code')
+ def test_mips64el_malta_5KEc_cpio(self):
+ """
+ :avocado: tags=arch:mips64el
+ :avocado: tags=machine:malta
+ :avocado: tags=endian:little
+ :avocado: tags=cpu:5KEc
+ """
+ kernel_url = ('https://github.com/philmd/qemu-testing-blob/'
+ 'raw/9ad2df38/mips/malta/mips64el/'
+ 'vmlinux-3.19.3.mtoman.20150408')
+ kernel_hash = '00d1d268fb9f7d8beda1de6bebcc46e884d71754'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+ initrd_url = ('https://github.com/groeck/linux-build-test/'
+ 'raw/8584a59e/rootfs/'
+ 'mipsel64/rootfs.mipsel64r1.cpio.gz')
+ initrd_hash = '1dbb8a396e916847325284dbe2151167'
+ initrd_path_gz = self.fetch_asset(initrd_url, algorithm='md5',
+ asset_hash=initrd_hash)
+ initrd_path = self.workdir + "rootfs.cpio"
+ archive.gzip_uncompress(initrd_path_gz, initrd_path)
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE
+ + 'console=ttyS0 console=tty '
+ + 'rdinit=/sbin/init noreboot')
+ self.vm.add_args('-kernel', kernel_path,
+ '-initrd', initrd_path,
+ '-append', kernel_command_line,
+ '-no-reboot')
+ self.vm.launch()
+ wait_for_console_pattern(self, 'Boot successful.')
+
+ exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo',
+ 'MIPS 5KE')
+ exec_command_and_wait_for_pattern(self, 'uname -a',
+ '3.19.3.mtoman.20150408')
+ exec_command_and_wait_for_pattern(self, 'reboot',
+ 'reboot: Restarting system')
+ # Wait for VM to shut down gracefully
+ self.vm.wait()
+
+ def do_test_mips_malta32el_nanomips(self, kernel_url, kernel_hash):
+ kernel_path_xz = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+ kernel_path = self.workdir + "kernel"
+ with lzma.open(kernel_path_xz, 'rb') as f_in:
+ with open(kernel_path, 'wb') as f_out:
+ shutil.copyfileobj(f_in, f_out)
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE
+ + 'mem=256m@@0x0 '
+ + 'console=ttyS0')
+ self.vm.add_args('-no-reboot',
+ '-kernel', kernel_path,
+ '-append', kernel_command_line)
+ self.vm.launch()
+ console_pattern = 'Kernel command line: %s' % kernel_command_line
+ self.wait_for_console_pattern(console_pattern)
+
+ def test_mips_malta32el_nanomips_4k(self):
+ """
+ :avocado: tags=arch:mipsel
+ :avocado: tags=machine:malta
+ :avocado: tags=endian:little
+ :avocado: tags=cpu:I7200
+ """
+ kernel_url = ('https://mipsdistros.mips.com/LinuxDistro/nanomips/'
+ 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/'
+ 'generic_nano32r6el_page4k.xz')
+ kernel_hash = '477456aafd2a0f1ddc9482727f20fe9575565dd6'
+ self.do_test_mips_malta32el_nanomips(kernel_url, kernel_hash)
+
+ def test_mips_malta32el_nanomips_16k_up(self):
+ """
+ :avocado: tags=arch:mipsel
+ :avocado: tags=machine:malta
+ :avocado: tags=endian:little
+ :avocado: tags=cpu:I7200
+ """
+ kernel_url = ('https://mipsdistros.mips.com/LinuxDistro/nanomips/'
+ 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/'
+ 'generic_nano32r6el_page16k_up.xz')
+ kernel_hash = 'e882868f944c71c816e832e2303b7874d044a7bc'
+ self.do_test_mips_malta32el_nanomips(kernel_url, kernel_hash)
+
+ def test_mips_malta32el_nanomips_64k_dbg(self):
+ """
+ :avocado: tags=arch:mipsel
+ :avocado: tags=machine:malta
+ :avocado: tags=endian:little
+ :avocado: tags=cpu:I7200
+ """
+ kernel_url = ('https://mipsdistros.mips.com/LinuxDistro/nanomips/'
+ 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/'
+ 'generic_nano32r6el_page64k_dbg.xz')
+ kernel_hash = '18d1c68f2e23429e266ca39ba5349ccd0aeb7180'
+ self.do_test_mips_malta32el_nanomips(kernel_url, kernel_hash)
+
+ def test_aarch64_xlnx_versal_virt(self):
+ """
+ :avocado: tags=arch:aarch64
+ :avocado: tags=machine:xlnx-versal-virt
+ :avocado: tags=device:pl011
+ :avocado: tags=device:arm_gicv3
+ :avocado: tags=accel:tcg
+ """
+ images_url = ('http://ports.ubuntu.com/ubuntu-ports/dists/'
+ 'bionic-updates/main/installer-arm64/'
+ '20101020ubuntu543.19/images/')
+ kernel_url = images_url + 'netboot/ubuntu-installer/arm64/linux'
+ kernel_hash = 'e167757620640eb26de0972f578741924abb3a82'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ initrd_url = images_url + 'netboot/ubuntu-installer/arm64/initrd.gz'
+ initrd_hash = 'cab5cb3fcefca8408aa5aae57f24574bfce8bdb9'
+ initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
+
+ self.vm.set_console()
+ self.vm.add_args('-m', '2G',
+ '-accel', 'tcg',
+ '-kernel', kernel_path,
+ '-initrd', initrd_path)
+ self.vm.launch()
+ self.wait_for_console_pattern('Checked W+X mappings: passed')
+
+ def test_arm_virt(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:virt
+ :avocado: tags=accel:tcg
+ """
+ kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora'
+ '/linux/releases/29/Everything/armhfp/os/images/pxeboot'
+ '/vmlinuz')
+ kernel_hash = 'e9826d741b4fb04cadba8d4824d1ed3b7fb8b4d4'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyAMA0')
+ self.vm.add_args('-kernel', kernel_path,
+ '-append', kernel_command_line)
+ self.vm.launch()
+ console_pattern = 'Kernel command line: %s' % kernel_command_line
+ self.wait_for_console_pattern(console_pattern)
+
+ def test_arm_emcraft_sf2(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:emcraft-sf2
+ :avocado: tags=endian:little
+ :avocado: tags=u-boot
+ :avocado: tags=accel:tcg
+ """
+ self.require_netdev('user')
+
+ uboot_url = ('https://raw.githubusercontent.com/'
+ 'Subbaraya-Sundeep/qemu-test-binaries/'
+ 'fe371d32e50ca682391e1e70ab98c2942aeffb01/u-boot')
+ uboot_hash = 'cbb8cbab970f594bf6523b9855be209c08374ae2'
+ uboot_path = self.fetch_asset(uboot_url, asset_hash=uboot_hash)
+ spi_url = ('https://raw.githubusercontent.com/'
+ 'Subbaraya-Sundeep/qemu-test-binaries/'
+ 'fe371d32e50ca682391e1e70ab98c2942aeffb01/spi.bin')
+ spi_hash = '65523a1835949b6f4553be96dec1b6a38fb05501'
+ spi_path = self.fetch_asset(spi_url, asset_hash=spi_hash)
+
+ file_truncate(spi_path, 16 << 20) # Spansion S25FL128SDPBHICO is 16 MiB
+
+ self.vm.set_console()
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE
+ self.vm.add_args('-kernel', uboot_path,
+ '-append', kernel_command_line,
+ '-drive', 'file=' + spi_path + ',if=mtd,format=raw',
+ '-no-reboot')
+ self.vm.launch()
+ self.wait_for_console_pattern('Enter \'help\' for a list')
+
+ exec_command_and_wait_for_pattern(self, 'ifconfig eth0 10.0.2.15',
+ 'eth0: link becomes ready')
+ exec_command_and_wait_for_pattern(self, 'ping -c 3 10.0.2.2',
+ '3 packets transmitted, 3 packets received, 0% packet loss')
+
+ def do_test_arm_raspi2(self, uart_id):
+ """
+ :avocado: tags=accel:tcg
+
+ The kernel can be rebuilt using the kernel source referenced
+ and following the instructions on the on:
+ https://www.raspberrypi.org/documentation/linux/kernel/building.md
+ """
+ serial_kernel_cmdline = {
+ 0: 'earlycon=pl011,0x3f201000 console=ttyAMA0',
+ }
+ deb_url = ('http://archive.raspberrypi.org/debian/'
+ 'pool/main/r/raspberrypi-firmware/'
+ 'raspberrypi-kernel_1.20190215-1_armhf.deb')
+ deb_hash = 'cd284220b32128c5084037553db3c482426f3972'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path, '/boot/kernel7.img')
+ dtb_path = self.extract_from_deb(deb_path, '/boot/bcm2709-rpi-2-b.dtb')
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ serial_kernel_cmdline[uart_id] +
+ ' root=/dev/mmcblk0p2 rootwait ' +
+ 'dwc_otg.fiq_fsm_enable=0')
+ self.vm.add_args('-kernel', kernel_path,
+ '-dtb', dtb_path,
+ '-append', kernel_command_line,
+ '-device', 'usb-kbd')
+ self.vm.launch()
+ console_pattern = 'Kernel command line: %s' % kernel_command_line
+ self.wait_for_console_pattern(console_pattern)
+ console_pattern = 'Product: QEMU USB Keyboard'
+ self.wait_for_console_pattern(console_pattern)
+
+ def test_arm_raspi2_uart0(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:raspi2b
+ :avocado: tags=device:pl011
+ :avocado: tags=accel:tcg
+ """
+ self.do_test_arm_raspi2(0)
+
+ def test_arm_raspi2_initrd(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:raspi2b
+ """
+ deb_url = ('http://archive.raspberrypi.org/debian/'
+ 'pool/main/r/raspberrypi-firmware/'
+ 'raspberrypi-kernel_1.20190215-1_armhf.deb')
+ deb_hash = 'cd284220b32128c5084037553db3c482426f3972'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path, '/boot/kernel7.img')
+ dtb_path = self.extract_from_deb(deb_path, '/boot/bcm2709-rpi-2-b.dtb')
+
+ initrd_url = ('https://github.com/groeck/linux-build-test/raw/'
+ '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/'
+ 'arm/rootfs-armv7a.cpio.gz')
+ initrd_hash = '604b2e45cdf35045846b8bbfbf2129b1891bdc9c'
+ initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
+ initrd_path = os.path.join(self.workdir, 'rootfs.cpio')
+ archive.gzip_uncompress(initrd_path_gz, initrd_path)
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'earlycon=pl011,0x3f201000 console=ttyAMA0 '
+ 'panic=-1 noreboot ' +
+ 'dwc_otg.fiq_fsm_enable=0')
+ self.vm.add_args('-kernel', kernel_path,
+ '-dtb', dtb_path,
+ '-initrd', initrd_path,
+ '-append', kernel_command_line,
+ '-no-reboot')
+ self.vm.launch()
+ self.wait_for_console_pattern('Boot successful.')
+
+ exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo',
+ 'BCM2835')
+ exec_command_and_wait_for_pattern(self, 'cat /proc/iomem',
+ '/soc/cprman@7e101000')
+ exec_command_and_wait_for_pattern(self, 'halt', 'reboot: System halted')
+ # Wait for VM to shut down gracefully
+ self.vm.wait()
+
+ def test_arm_raspi4(self):
+ """
+ :avocado: tags=arch:aarch64
+ :avocado: tags=machine:raspi4b
+ :avocado: tags=device:pl011
+ :avocado: tags=accel:tcg
+ :avocado: tags=rpi4b
+
+ The kernel can be rebuilt using the kernel source referenced
+ and following the instructions on the on:
+ https://www.raspberrypi.org/documentation/linux/kernel/building.md
+ """
+
+ deb_url = ('http://archive.raspberrypi.org/debian/'
+ 'pool/main/r/raspberrypi-firmware/'
+ 'raspberrypi-kernel_1.20230106-1_arm64.deb')
+ deb_hash = '08dc55696535b18a6d4fe6fa10d4c0d905cbb2ed'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path, '/boot/kernel8.img')
+ dtb_path = self.extract_from_deb(deb_path, '/boot/bcm2711-rpi-4-b.dtb')
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'earlycon=pl011,mmio32,0xfe201000 ' +
+ 'console=ttyAMA0,115200 ' +
+ 'root=/dev/mmcblk1p2 rootwait ' +
+ 'dwc_otg.fiq_fsm_enable=0')
+ self.vm.add_args('-kernel', kernel_path,
+ '-dtb', dtb_path,
+ '-append', kernel_command_line)
+ # When PCI is supported we can add a USB controller:
+ # '-device', 'qemu-xhci,bus=pcie.1,id=xhci',
+ # '-device', 'usb-kbd,bus=xhci.0',
+ self.vm.launch()
+ console_pattern = 'Kernel command line: %s' % kernel_command_line
+ self.wait_for_console_pattern(console_pattern)
+ # When USB is enabled we can look for this
+ # console_pattern = 'Product: QEMU USB Keyboard'
+ # self.wait_for_console_pattern(console_pattern)
+ console_pattern = 'Waiting for root device'
+ self.wait_for_console_pattern(console_pattern)
+
+
+ def test_arm_raspi4_initrd(self):
+ """
+ :avocado: tags=arch:aarch64
+ :avocado: tags=machine:raspi4b
+ :avocado: tags=device:pl011
+ :avocado: tags=accel:tcg
+ :avocado: tags=rpi4b
+
+ The kernel can be rebuilt using the kernel source referenced
+ and following the instructions on the on:
+ https://www.raspberrypi.org/documentation/linux/kernel/building.md
+ """
+ deb_url = ('http://archive.raspberrypi.org/debian/'
+ 'pool/main/r/raspberrypi-firmware/'
+ 'raspberrypi-kernel_1.20230106-1_arm64.deb')
+ deb_hash = '08dc55696535b18a6d4fe6fa10d4c0d905cbb2ed'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path, '/boot/kernel8.img')
+ dtb_path = self.extract_from_deb(deb_path, '/boot/bcm2711-rpi-4-b.dtb')
+
+ initrd_url = ('https://github.com/groeck/linux-build-test/raw/'
+ '86b2be1384d41c8c388e63078a847f1e1c4cb1de/rootfs/'
+ 'arm64/rootfs.cpio.gz')
+ initrd_hash = 'f3d4f9fa92a49aa542f1b44d34be77bbf8ca5b9d'
+ initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
+ initrd_path = os.path.join(self.workdir, 'rootfs.cpio')
+ archive.gzip_uncompress(initrd_path_gz, initrd_path)
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'earlycon=pl011,mmio32,0xfe201000 ' +
+ 'console=ttyAMA0,115200 ' +
+ 'panic=-1 noreboot ' +
+ 'dwc_otg.fiq_fsm_enable=0')
+ self.vm.add_args('-kernel', kernel_path,
+ '-dtb', dtb_path,
+ '-initrd', initrd_path,
+ '-append', kernel_command_line,
+ '-no-reboot')
+ # When PCI is supported we can add a USB controller:
+ # '-device', 'qemu-xhci,bus=pcie.1,id=xhci',
+ # '-device', 'usb-kbd,bus=xhci.0',
+ self.vm.launch()
+ self.wait_for_console_pattern('Boot successful.')
+
+ exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo',
+ 'BCM2835')
+ exec_command_and_wait_for_pattern(self, 'cat /proc/iomem',
+ 'cprman@7e101000')
+ exec_command_and_wait_for_pattern(self, 'halt', 'reboot: System halted')
+ # TODO: Raspberry Pi4 doesn't shut down properly with recent kernels
+ # Wait for VM to shut down gracefully
+ #self.vm.wait()
+
+ def test_arm_exynos4210_initrd(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:smdkc210
+ :avocado: tags=accel:tcg
+ """
+ deb_url = ('https://snapshot.debian.org/archive/debian/'
+ '20190928T224601Z/pool/main/l/linux/'
+ 'linux-image-4.19.0-6-armmp_4.19.67-2+deb10u1_armhf.deb')
+ deb_hash = 'fa9df4a0d38936cb50084838f2cb933f570d7d82'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path,
+ '/boot/vmlinuz-4.19.0-6-armmp')
+ dtb_path = '/usr/lib/linux-image-4.19.0-6-armmp/exynos4210-smdkv310.dtb'
+ dtb_path = self.extract_from_deb(deb_path, dtb_path)
+
+ initrd_url = ('https://github.com/groeck/linux-build-test/raw/'
+ '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/'
+ 'arm/rootfs-armv5.cpio.gz')
+ initrd_hash = '2b50f1873e113523967806f4da2afe385462ff9b'
+ initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
+ initrd_path = os.path.join(self.workdir, 'rootfs.cpio')
+ archive.gzip_uncompress(initrd_path_gz, initrd_path)
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'earlycon=exynos4210,0x13800000 earlyprintk ' +
+ 'console=ttySAC0,115200n8 ' +
+ 'random.trust_cpu=off cryptomgr.notests ' +
+ 'cpuidle.off=1 panic=-1 noreboot')
+
+ self.vm.add_args('-kernel', kernel_path,
+ '-dtb', dtb_path,
+ '-initrd', initrd_path,
+ '-append', kernel_command_line,
+ '-no-reboot')
+ self.vm.launch()
+
+ self.wait_for_console_pattern('Boot successful.')
+ # TODO user command, for now the uart is stuck
+
+ def test_arm_cubieboard_initrd(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:cubieboard
+ :avocado: tags=accel:tcg
+ """
+ deb_url = ('https://apt.armbian.com/pool/main/l/'
+ 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb')
+ deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path,
+ '/boot/vmlinuz-5.10.16-sunxi')
+ dtb_path = '/usr/lib/linux-image-current-sunxi/sun4i-a10-cubieboard.dtb'
+ dtb_path = self.extract_from_deb(deb_path, dtb_path)
+ initrd_url = ('https://github.com/groeck/linux-build-test/raw/'
+ '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/'
+ 'arm/rootfs-armv5.cpio.gz')
+ initrd_hash = '2b50f1873e113523967806f4da2afe385462ff9b'
+ initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
+ initrd_path = os.path.join(self.workdir, 'rootfs.cpio')
+ archive.gzip_uncompress(initrd_path_gz, initrd_path)
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyS0,115200 '
+ 'usbcore.nousb '
+ 'panic=-1 noreboot')
+ self.vm.add_args('-kernel', kernel_path,
+ '-dtb', dtb_path,
+ '-initrd', initrd_path,
+ '-append', kernel_command_line,
+ '-no-reboot')
+ self.vm.launch()
+ self.wait_for_console_pattern('Boot successful.')
+
+ exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo',
+ 'Allwinner sun4i/sun5i')
+ exec_command_and_wait_for_pattern(self, 'cat /proc/iomem',
+ 'system-control@1c00000')
+ exec_command_and_wait_for_pattern(self, 'reboot',
+ 'reboot: Restarting system')
+ # Wait for VM to shut down gracefully
+ self.vm.wait()
+
+ def test_arm_cubieboard_sata(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:cubieboard
+ :avocado: tags=accel:tcg
+ """
+ deb_url = ('https://apt.armbian.com/pool/main/l/'
+ 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb')
+ deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path,
+ '/boot/vmlinuz-5.10.16-sunxi')
+ dtb_path = '/usr/lib/linux-image-current-sunxi/sun4i-a10-cubieboard.dtb'
+ dtb_path = self.extract_from_deb(deb_path, dtb_path)
+ rootfs_url = ('https://github.com/groeck/linux-build-test/raw/'
+ '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/'
+ 'arm/rootfs-armv5.ext2.gz')
+ rootfs_hash = '093e89d2b4d982234bf528bc9fb2f2f17a9d1f93'
+ rootfs_path_gz = self.fetch_asset(rootfs_url, asset_hash=rootfs_hash)
+ rootfs_path = os.path.join(self.workdir, 'rootfs.cpio')
+ archive.gzip_uncompress(rootfs_path_gz, rootfs_path)
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyS0,115200 '
+ 'usbcore.nousb '
+ 'root=/dev/sda ro '
+ 'panic=-1 noreboot')
+ self.vm.add_args('-kernel', kernel_path,
+ '-dtb', dtb_path,
+ '-drive', 'if=none,format=raw,id=disk0,file='
+ + rootfs_path,
+ '-device', 'ide-hd,bus=ide.0,drive=disk0',
+ '-append', kernel_command_line,
+ '-no-reboot')
+ self.vm.launch()
+ self.wait_for_console_pattern('Boot successful.')
+
+ exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo',
+ 'Allwinner sun4i/sun5i')
+ exec_command_and_wait_for_pattern(self, 'cat /proc/partitions',
+ 'sda')
+ exec_command_and_wait_for_pattern(self, 'reboot',
+ 'reboot: Restarting system')
+ # Wait for VM to shut down gracefully
+ self.vm.wait()
+
+ @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited')
+ def test_arm_cubieboard_openwrt_22_03_2(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:cubieboard
+ :avocado: tags=device:sd
+ """
+
+ # This test download a 7.5 MiB compressed image and expand it
+ # to 126 MiB.
+ image_url = ('https://downloads.openwrt.org/releases/22.03.2/targets/'
+ 'sunxi/cortexa8/openwrt-22.03.2-sunxi-cortexa8-'
+ 'cubietech_a10-cubieboard-ext4-sdcard.img.gz')
+ image_hash = ('94b5ecbfbc0b3b56276e5146b899eafa'
+ '2ac5dc2d08733d6705af9f144f39f554')
+ image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash,
+ algorithm='sha256')
+ image_path = archive.extract(image_path_gz, self.workdir)
+ image_pow2ceil_expand(image_path)
+
+ self.vm.set_console()
+ self.vm.add_args('-drive', 'file=' + image_path + ',if=sd,format=raw',
+ '-nic', 'user',
+ '-no-reboot')
+ self.vm.launch()
+
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'usbcore.nousb '
+ 'noreboot')
+
+ self.wait_for_console_pattern('U-Boot SPL')
+
+ interrupt_interactive_console_until_pattern(
+ self, 'Hit any key to stop autoboot:', '=>')
+ exec_command_and_wait_for_pattern(self, "setenv extraargs '" +
+ kernel_command_line + "'", '=>')
+ exec_command_and_wait_for_pattern(self, 'boot', 'Starting kernel ...');
+
+ self.wait_for_console_pattern(
+ 'Please press Enter to activate this console.')
+
+ exec_command_and_wait_for_pattern(self, ' ', 'root@')
+
+ exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo',
+ 'Allwinner sun4i/sun5i')
+ exec_command_and_wait_for_pattern(self, 'reboot',
+ 'reboot: Restarting system')
+ # Wait for VM to shut down gracefully
+ self.vm.wait()
+
+ @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout')
+ def test_arm_quanta_gsj(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:quanta-gsj
+ :avocado: tags=accel:tcg
+ """
+ # 25 MiB compressed, 32 MiB uncompressed.
+ image_url = (
+ 'https://github.com/hskinnemoen/openbmc/releases/download/'
+ '20200711-gsj-qemu-0/obmc-phosphor-image-gsj.static.mtd.gz')
+ image_hash = '14895e634923345cb5c8776037ff7876df96f6b1'
+ image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash)
+ image_name = 'obmc.mtd'
+ image_path = os.path.join(self.workdir, image_name)
+ archive.gzip_uncompress(image_path_gz, image_path)
+
+ self.vm.set_console()
+ drive_args = 'file=' + image_path + ',if=mtd,bus=0,unit=0'
+ self.vm.add_args('-drive', drive_args)
+ self.vm.launch()
+
+ # Disable drivers and services that stall for a long time during boot,
+ # to avoid running past the 90-second timeout. These may be removed
+ # as the corresponding device support is added.
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + (
+ 'console=${console} '
+ 'mem=${mem} '
+ 'initcall_blacklist=npcm_i2c_bus_driver_init '
+ 'systemd.mask=systemd-random-seed.service '
+ 'systemd.mask=dropbearkey.service '
+ )
+
+ self.wait_for_console_pattern('> BootBlock by Nuvoton')
+ self.wait_for_console_pattern('>Device: Poleg BMC NPCM730')
+ self.wait_for_console_pattern('>Skip DDR init.')
+ self.wait_for_console_pattern('U-Boot ')
+ interrupt_interactive_console_until_pattern(
+ self, 'Hit any key to stop autoboot:', 'U-Boot>')
+ exec_command_and_wait_for_pattern(
+ self, "setenv bootargs ${bootargs} " + kernel_command_line,
+ 'U-Boot>')
+ exec_command_and_wait_for_pattern(
+ self, 'run romboot', 'Booting Kernel from flash')
+ self.wait_for_console_pattern('Booting Linux on physical CPU 0x0')
+ self.wait_for_console_pattern('CPU1: thread -1, cpu 1, socket 0')
+ self.wait_for_console_pattern('OpenBMC Project Reference Distro')
+ self.wait_for_console_pattern('gsj login:')
+
+ def test_arm_quanta_gsj_initrd(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:quanta-gsj
+ :avocado: tags=accel:tcg
+ """
+ initrd_url = (
+ 'https://github.com/hskinnemoen/openbmc/releases/download/'
+ '20200711-gsj-qemu-0/obmc-phosphor-initramfs-gsj.cpio.xz')
+ initrd_hash = '98fefe5d7e56727b1eb17d5c00311b1b5c945300'
+ initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
+ kernel_url = (
+ 'https://github.com/hskinnemoen/openbmc/releases/download/'
+ '20200711-gsj-qemu-0/uImage-gsj.bin')
+ kernel_hash = 'fa67b2f141d56d39b3c54305c0e8a899c99eb2c7'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+ dtb_url = (
+ 'https://github.com/hskinnemoen/openbmc/releases/download/'
+ '20200711-gsj-qemu-0/nuvoton-npcm730-gsj.dtb')
+ dtb_hash = '18315f7006d7b688d8312d5c727eecd819aa36a4'
+ dtb_path = self.fetch_asset(dtb_url, asset_hash=dtb_hash)
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyS0,115200n8 '
+ 'earlycon=uart8250,mmio32,0xf0001000')
+ self.vm.add_args('-kernel', kernel_path,
+ '-initrd', initrd_path,
+ '-dtb', dtb_path,
+ '-append', kernel_command_line)
+ self.vm.launch()
+
+ self.wait_for_console_pattern('Booting Linux on physical CPU 0x0')
+ self.wait_for_console_pattern('CPU1: thread -1, cpu 1, socket 0')
+ self.wait_for_console_pattern(
+ 'Give root password for system maintenance')
+
+ def test_arm_bpim2u(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:bpim2u
+ :avocado: tags=accel:tcg
+ """
+ deb_url = ('https://apt.armbian.com/pool/main/l/linux-5.10.16-sunxi/'
+ 'linux-image-current-sunxi_21.02.2_armhf.deb')
+ deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path,
+ '/boot/vmlinuz-5.10.16-sunxi')
+ dtb_path = ('/usr/lib/linux-image-current-sunxi/'
+ 'sun8i-r40-bananapi-m2-ultra.dtb')
+ dtb_path = self.extract_from_deb(deb_path, dtb_path)
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyS0,115200n8 '
+ 'earlycon=uart,mmio32,0x1c28000')
+ self.vm.add_args('-kernel', kernel_path,
+ '-dtb', dtb_path,
+ '-append', kernel_command_line)
+ self.vm.launch()
+ console_pattern = 'Kernel command line: %s' % kernel_command_line
+ self.wait_for_console_pattern(console_pattern)
+
+ def test_arm_bpim2u_initrd(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=accel:tcg
+ :avocado: tags=machine:bpim2u
+ """
+ deb_url = ('https://apt.armbian.com/pool/main/l/linux-5.10.16-sunxi/'
+ 'linux-image-current-sunxi_21.02.2_armhf.deb')
+ deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path,
+ '/boot/vmlinuz-5.10.16-sunxi')
+ dtb_path = ('/usr/lib/linux-image-current-sunxi/'
+ 'sun8i-r40-bananapi-m2-ultra.dtb')
+ dtb_path = self.extract_from_deb(deb_path, dtb_path)
+ initrd_url = ('https://github.com/groeck/linux-build-test/raw/'
+ '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/'
+ 'arm/rootfs-armv7a.cpio.gz')
+ initrd_hash = '604b2e45cdf35045846b8bbfbf2129b1891bdc9c'
+ initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
+ initrd_path = os.path.join(self.workdir, 'rootfs.cpio')
+ archive.gzip_uncompress(initrd_path_gz, initrd_path)
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyS0,115200 '
+ 'panic=-1 noreboot')
+ self.vm.add_args('-kernel', kernel_path,
+ '-dtb', dtb_path,
+ '-initrd', initrd_path,
+ '-append', kernel_command_line,
+ '-no-reboot')
+ self.vm.launch()
+ self.wait_for_console_pattern('Boot successful.')
+
+ exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo',
+ 'Allwinner sun8i Family')
+ exec_command_and_wait_for_pattern(self, 'cat /proc/iomem',
+ 'system-control@1c00000')
+ exec_command_and_wait_for_pattern(self, 'reboot',
+ 'reboot: Restarting system')
+ # Wait for VM to shut down gracefully
+ self.vm.wait()
+
+ def test_arm_bpim2u_gmac(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=accel:tcg
+ :avocado: tags=machine:bpim2u
+ :avocado: tags=device:sd
+ """
+ self.require_netdev('user')
+
+ deb_url = ('https://apt.armbian.com/pool/main/l/linux-5.10.16-sunxi/'
+ 'linux-image-current-sunxi_21.02.2_armhf.deb')
+ deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path,
+ '/boot/vmlinuz-5.10.16-sunxi')
+ dtb_path = ('/usr/lib/linux-image-current-sunxi/'
+ 'sun8i-r40-bananapi-m2-ultra.dtb')
+ dtb_path = self.extract_from_deb(deb_path, dtb_path)
+ rootfs_url = ('http://storage.kernelci.org/images/rootfs/buildroot/'
+ 'buildroot-baseline/20221116.0/armel/rootfs.ext2.xz')
+ rootfs_hash = 'fae32f337c7b87547b10f42599acf109da8b6d9a'
+ rootfs_path_xz = self.fetch_asset(rootfs_url, asset_hash=rootfs_hash)
+ rootfs_path = os.path.join(self.workdir, 'rootfs.cpio')
+ archive.lzma_uncompress(rootfs_path_xz, rootfs_path)
+ image_pow2ceil_expand(rootfs_path)
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyS0,115200 '
+ 'root=b300 rootwait rw '
+ 'panic=-1 noreboot')
+ self.vm.add_args('-kernel', kernel_path,
+ '-dtb', dtb_path,
+ '-drive', 'file=' + rootfs_path + ',if=sd,format=raw',
+ '-net', 'nic,model=gmac,netdev=host_gmac',
+ '-netdev', 'user,id=host_gmac',
+ '-append', kernel_command_line,
+ '-no-reboot')
+ self.vm.launch()
+ shell_ready = "/bin/sh: can't access tty; job control turned off"
+ self.wait_for_console_pattern(shell_ready)
+
+ exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo',
+ 'Allwinner sun8i Family')
+ exec_command_and_wait_for_pattern(self, 'cat /proc/partitions',
+ 'mmcblk')
+ exec_command_and_wait_for_pattern(self, 'ifconfig eth0 up',
+ 'eth0: Link is Up')
+ exec_command_and_wait_for_pattern(self, 'udhcpc eth0',
+ 'udhcpc: lease of 10.0.2.15 obtained')
+ exec_command_and_wait_for_pattern(self, 'ping -c 3 10.0.2.2',
+ '3 packets transmitted, 3 packets received, 0% packet loss')
+ exec_command_and_wait_for_pattern(self, 'reboot',
+ 'reboot: Restarting system')
+ # Wait for VM to shut down gracefully
+ self.vm.wait()
+
+ @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited')
+ def test_arm_bpim2u_openwrt_22_03_3(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:bpim2u
+ :avocado: tags=device:sd
+ """
+
+ # This test download a 8.9 MiB compressed image and expand it
+ # to 127 MiB.
+ image_url = ('https://downloads.openwrt.org/releases/22.03.3/targets/'
+ 'sunxi/cortexa7/openwrt-22.03.3-sunxi-cortexa7-'
+ 'sinovoip_bananapi-m2-ultra-ext4-sdcard.img.gz')
+ image_hash = ('5b41b4e11423e562c6011640f9a7cd3b'
+ 'dd0a3d42b83430f7caa70a432e6cd82c')
+ image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash,
+ algorithm='sha256')
+ image_path = archive.extract(image_path_gz, self.workdir)
+ image_pow2ceil_expand(image_path)
+
+ self.vm.set_console()
+ self.vm.add_args('-drive', 'file=' + image_path + ',if=sd,format=raw',
+ '-nic', 'user',
+ '-no-reboot')
+ self.vm.launch()
+
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'usbcore.nousb '
+ 'noreboot')
+
+ self.wait_for_console_pattern('U-Boot SPL')
+
+ interrupt_interactive_console_until_pattern(
+ self, 'Hit any key to stop autoboot:', '=>')
+ exec_command_and_wait_for_pattern(self, "setenv extraargs '" +
+ kernel_command_line + "'", '=>')
+ exec_command_and_wait_for_pattern(self, 'boot', 'Starting kernel ...');
+
+ self.wait_for_console_pattern(
+ 'Please press Enter to activate this console.')
+
+ exec_command_and_wait_for_pattern(self, ' ', 'root@')
+
+ exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo',
+ 'Allwinner sun8i Family')
+ exec_command_and_wait_for_pattern(self, 'cat /proc/iomem',
+ 'system-control@1c00000')
+
+ def test_arm_orangepi(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:orangepi-pc
+ :avocado: tags=accel:tcg
+ """
+ deb_url = ('https://apt.armbian.com/pool/main/l/'
+ 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb')
+ deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path,
+ '/boot/vmlinuz-5.10.16-sunxi')
+ dtb_path = '/usr/lib/linux-image-current-sunxi/sun8i-h3-orangepi-pc.dtb'
+ dtb_path = self.extract_from_deb(deb_path, dtb_path)
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyS0,115200n8 '
+ 'earlycon=uart,mmio32,0x1c28000')
+ self.vm.add_args('-kernel', kernel_path,
+ '-dtb', dtb_path,
+ '-append', kernel_command_line)
+ self.vm.launch()
+ console_pattern = 'Kernel command line: %s' % kernel_command_line
+ self.wait_for_console_pattern(console_pattern)
+
+ def test_arm_orangepi_initrd(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=accel:tcg
+ :avocado: tags=machine:orangepi-pc
+ """
+ deb_url = ('https://apt.armbian.com/pool/main/l/'
+ 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb')
+ deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path,
+ '/boot/vmlinuz-5.10.16-sunxi')
+ dtb_path = '/usr/lib/linux-image-current-sunxi/sun8i-h3-orangepi-pc.dtb'
+ dtb_path = self.extract_from_deb(deb_path, dtb_path)
+ initrd_url = ('https://github.com/groeck/linux-build-test/raw/'
+ '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/'
+ 'arm/rootfs-armv7a.cpio.gz')
+ initrd_hash = '604b2e45cdf35045846b8bbfbf2129b1891bdc9c'
+ initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
+ initrd_path = os.path.join(self.workdir, 'rootfs.cpio')
+ archive.gzip_uncompress(initrd_path_gz, initrd_path)
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyS0,115200 '
+ 'panic=-1 noreboot')
+ self.vm.add_args('-kernel', kernel_path,
+ '-dtb', dtb_path,
+ '-initrd', initrd_path,
+ '-append', kernel_command_line,
+ '-no-reboot')
+ self.vm.launch()
+ self.wait_for_console_pattern('Boot successful.')
+
+ exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo',
+ 'Allwinner sun8i Family')
+ exec_command_and_wait_for_pattern(self, 'cat /proc/iomem',
+ 'system-control@1c00000')
+ exec_command_and_wait_for_pattern(self, 'reboot',
+ 'reboot: Restarting system')
+ # Wait for VM to shut down gracefully
+ self.vm.wait()
+
+ def test_arm_orangepi_sd(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=accel:tcg
+ :avocado: tags=machine:orangepi-pc
+ :avocado: tags=device:sd
+ """
+ self.require_netdev('user')
+
+ deb_url = ('https://apt.armbian.com/pool/main/l/'
+ 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb')
+ deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path,
+ '/boot/vmlinuz-5.10.16-sunxi')
+ dtb_path = '/usr/lib/linux-image-current-sunxi/sun8i-h3-orangepi-pc.dtb'
+ dtb_path = self.extract_from_deb(deb_path, dtb_path)
+ rootfs_url = ('http://storage.kernelci.org/images/rootfs/buildroot/'
+ 'buildroot-baseline/20221116.0/armel/rootfs.ext2.xz')
+ rootfs_hash = 'fae32f337c7b87547b10f42599acf109da8b6d9a'
+ rootfs_path_xz = self.fetch_asset(rootfs_url, asset_hash=rootfs_hash)
+ rootfs_path = os.path.join(self.workdir, 'rootfs.cpio')
+ archive.lzma_uncompress(rootfs_path_xz, rootfs_path)
+ image_pow2ceil_expand(rootfs_path)
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyS0,115200 '
+ 'root=/dev/mmcblk0 rootwait rw '
+ 'panic=-1 noreboot')
+ self.vm.add_args('-kernel', kernel_path,
+ '-dtb', dtb_path,
+ '-drive', 'file=' + rootfs_path + ',if=sd,format=raw',
+ '-append', kernel_command_line,
+ '-no-reboot')
+ self.vm.launch()
+ shell_ready = "/bin/sh: can't access tty; job control turned off"
+ self.wait_for_console_pattern(shell_ready)
+
+ exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo',
+ 'Allwinner sun8i Family')
+ exec_command_and_wait_for_pattern(self, 'cat /proc/partitions',
+ 'mmcblk0')
+ exec_command_and_wait_for_pattern(self, 'ifconfig eth0 up',
+ 'eth0: Link is Up')
+ exec_command_and_wait_for_pattern(self, 'udhcpc eth0',
+ 'udhcpc: lease of 10.0.2.15 obtained')
+ exec_command_and_wait_for_pattern(self, 'ping -c 3 10.0.2.2',
+ '3 packets transmitted, 3 packets received, 0% packet loss')
+ exec_command_and_wait_for_pattern(self, 'reboot',
+ 'reboot: Restarting system')
+ # Wait for VM to shut down gracefully
+ self.vm.wait()
+
+ @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited')
+ def test_arm_orangepi_bionic_20_08(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:orangepi-pc
+ :avocado: tags=device:sd
+ """
+
+ # This test download a 275 MiB compressed image and expand it
+ # to 1036 MiB, but the underlying filesystem is 1552 MiB...
+ # As we expand it to 2 GiB we are safe.
+
+ image_url = ('https://archive.armbian.com/orangepipc/archive/'
+ 'Armbian_20.08.1_Orangepipc_bionic_current_5.8.5.img.xz')
+ image_hash = ('b4d6775f5673486329e45a0586bf06b6'
+ 'dbe792199fd182ac6b9c7bb6c7d3e6dd')
+ image_path_xz = self.fetch_asset(image_url, asset_hash=image_hash,
+ algorithm='sha256')
+ image_path = archive.extract(image_path_xz, self.workdir)
+ image_pow2ceil_expand(image_path)
+
+ self.vm.set_console()
+ self.vm.add_args('-drive', 'file=' + image_path + ',if=sd,format=raw',
+ '-nic', 'user',
+ '-no-reboot')
+ self.vm.launch()
+
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyS0,115200 '
+ 'loglevel=7 '
+ 'nosmp '
+ 'systemd.default_timeout_start_sec=9000 '
+ 'systemd.mask=armbian-zram-config.service '
+ 'systemd.mask=armbian-ramlog.service')
+
+ self.wait_for_console_pattern('U-Boot SPL')
+ self.wait_for_console_pattern('Autoboot in ')
+ exec_command_and_wait_for_pattern(self, ' ', '=>')
+ exec_command_and_wait_for_pattern(self, "setenv extraargs '" +
+ kernel_command_line + "'", '=>')
+ exec_command_and_wait_for_pattern(self, 'boot', 'Starting kernel ...');
+
+ self.wait_for_console_pattern('systemd[1]: Set hostname ' +
+ 'to <orangepipc>')
+ self.wait_for_console_pattern('Starting Load Kernel Modules...')
+
+ @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited')
+ def test_arm_orangepi_uboot_netbsd9(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:orangepi-pc
+ :avocado: tags=device:sd
+ :avocado: tags=os:netbsd
+ """
+ # This test download a 304MB compressed image and expand it to 2GB
+ deb_url = ('http://snapshot.debian.org/archive/debian/'
+ '20200108T145233Z/pool/main/u/u-boot/'
+ 'u-boot-sunxi_2020.01%2Bdfsg-1_armhf.deb')
+ deb_hash = 'f67f404a80753ca3d1258f13e38f2b060e13db99'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ # We use the common OrangePi PC 'plus' build of U-Boot for our secondary
+ # program loader (SPL). We will then set the path to the more specific
+ # OrangePi "PC" device tree blob with 'setenv fdtfile' in U-Boot prompt,
+ # before to boot NetBSD.
+ uboot_path = '/usr/lib/u-boot/orangepi_plus/u-boot-sunxi-with-spl.bin'
+ uboot_path = self.extract_from_deb(deb_path, uboot_path)
+ image_url = ('https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.0/'
+ 'evbarm-earmv7hf/binary/gzimg/armv7.img.gz')
+ image_hash = '2babb29d36d8360adcb39c09e31060945259917a'
+ image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash)
+ image_path = os.path.join(self.workdir, 'armv7.img')
+ archive.gzip_uncompress(image_path_gz, image_path)
+ image_pow2ceil_expand(image_path)
+ image_drive_args = 'if=sd,format=raw,snapshot=on,file=' + image_path
+
+ # dd if=u-boot-sunxi-with-spl.bin of=armv7.img bs=1K seek=8 conv=notrunc
+ with open(uboot_path, 'rb') as f_in:
+ with open(image_path, 'r+b') as f_out:
+ f_out.seek(8 * 1024)
+ shutil.copyfileobj(f_in, f_out)
+
+ self.vm.set_console()
+ self.vm.add_args('-nic', 'user',
+ '-drive', image_drive_args,
+ '-global', 'allwinner-rtc.base-year=2000',
+ '-no-reboot')
+ self.vm.launch()
+ wait_for_console_pattern(self, 'U-Boot 2020.01+dfsg-1')
+ interrupt_interactive_console_until_pattern(self,
+ 'Hit any key to stop autoboot:',
+ 'switch to partitions #0, OK')
+
+ exec_command_and_wait_for_pattern(self, '', '=>')
+ cmd = 'setenv bootargs root=ld0a'
+ exec_command_and_wait_for_pattern(self, cmd, '=>')
+ cmd = 'setenv kernel netbsd-GENERIC.ub'
+ exec_command_and_wait_for_pattern(self, cmd, '=>')
+ cmd = 'setenv fdtfile dtb/sun8i-h3-orangepi-pc.dtb'
+ exec_command_and_wait_for_pattern(self, cmd, '=>')
+ cmd = ("setenv bootcmd 'fatload mmc 0:1 ${kernel_addr_r} ${kernel}; "
+ "fatload mmc 0:1 ${fdt_addr_r} ${fdtfile}; "
+ "fdt addr ${fdt_addr_r}; "
+ "bootm ${kernel_addr_r} - ${fdt_addr_r}'")
+ exec_command_and_wait_for_pattern(self, cmd, '=>')
+
+ exec_command_and_wait_for_pattern(self, 'boot',
+ 'Booting kernel from Legacy Image')
+ wait_for_console_pattern(self, 'Starting kernel ...')
+ wait_for_console_pattern(self, 'NetBSD 9.0 (GENERIC)')
+ # Wait for user-space
+ wait_for_console_pattern(self, 'Starting root file system check')
+
+ def test_aarch64_raspi3_atf(self):
+ """
+ :avocado: tags=accel:tcg
+ :avocado: tags=arch:aarch64
+ :avocado: tags=machine:raspi3b
+ :avocado: tags=cpu:cortex-a53
+ :avocado: tags=device:pl011
+ :avocado: tags=atf
+ """
+ zip_url = ('https://github.com/pbatard/RPi3/releases/download/'
+ 'v1.15/RPi3_UEFI_Firmware_v1.15.zip')
+ zip_hash = '74b3bd0de92683cadb14e008a7575e1d0c3cafb9'
+ zip_path = self.fetch_asset(zip_url, asset_hash=zip_hash)
+
+ archive.extract(zip_path, self.workdir)
+ efi_fd = os.path.join(self.workdir, 'RPI_EFI.fd')
+
+ self.vm.set_console(console_index=1)
+ self.vm.add_args('-nodefaults',
+ '-device', 'loader,file=%s,force-raw=true' % efi_fd)
+ self.vm.launch()
+ self.wait_for_console_pattern('version UEFI Firmware v1.15')
+
+ def test_s390x_s390_ccw_virtio(self):
+ """
+ :avocado: tags=arch:s390x
+ :avocado: tags=machine:s390-ccw-virtio
+ """
+ kernel_url = ('https://archives.fedoraproject.org/pub/archive'
+ '/fedora-secondary/releases/29/Everything/s390x/os/images'
+ '/kernel.img')
+ kernel_hash = 'e8e8439103ef8053418ef062644ffd46a7919313'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ self.vm.set_console()
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=sclp0'
+ self.vm.add_args('-nodefaults',
+ '-kernel', kernel_path,
+ '-append', kernel_command_line)
+ self.vm.launch()
+ console_pattern = 'Kernel command line: %s' % kernel_command_line
+ self.wait_for_console_pattern(console_pattern)
+
+ def test_alpha_clipper(self):
+ """
+ :avocado: tags=arch:alpha
+ :avocado: tags=machine:clipper
+ """
+ kernel_url = ('http://archive.debian.org/debian/dists/lenny/main/'
+ 'installer-alpha/20090123lenny10/images/cdrom/vmlinuz')
+ kernel_hash = '3a943149335529e2ed3e74d0d787b85fb5671ba3'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ uncompressed_kernel = archive.uncompress(kernel_path, self.workdir)
+
+ self.vm.set_console()
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0'
+ self.vm.add_args('-nodefaults',
+ '-kernel', uncompressed_kernel,
+ '-append', kernel_command_line)
+ self.vm.launch()
+ console_pattern = 'Kernel command line: %s' % kernel_command_line
+ self.wait_for_console_pattern(console_pattern)
+
+ def test_m68k_q800(self):
+ """
+ :avocado: tags=arch:m68k
+ :avocado: tags=machine:q800
+ """
+ deb_url = ('https://snapshot.debian.org/archive/debian-ports'
+ '/20191021T083923Z/pool-m68k/main'
+ '/l/linux/kernel-image-5.3.0-1-m68k-di_5.3.7-1_m68k.udeb')
+ deb_hash = '044954bb9be4160a3ce81f8bc1b5e856b75cccd1'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path,
+ '/boot/vmlinux-5.3.0-1-m68k')
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyS0 vga=off')
+ self.vm.add_args('-kernel', kernel_path,
+ '-append', kernel_command_line)
+ self.vm.launch()
+ console_pattern = 'Kernel command line: %s' % kernel_command_line
+ self.wait_for_console_pattern(console_pattern)
+ console_pattern = 'No filesystem could mount root'
+ self.wait_for_console_pattern(console_pattern)
+
+ def do_test_advcal_2018(self, day, tar_hash, kernel_name, console=0):
+ tar_url = ('https://qemu-advcal.gitlab.io'
+ '/qac-best-of-multiarch/download/day' + day + '.tar.xz')
+ file_path = self.fetch_asset(tar_url, asset_hash=tar_hash)
+ archive.extract(file_path, self.workdir)
+ self.vm.set_console(console_index=console)
+ self.vm.add_args('-kernel',
+ self.workdir + '/day' + day + '/' + kernel_name)
+ self.vm.launch()
+ self.wait_for_console_pattern('QEMU advent calendar')
+
+ def test_arm_vexpressa9(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:vexpress-a9
+ """
+ tar_hash = '32b7677ce8b6f1471fb0059865f451169934245b'
+ self.vm.add_args('-dtb', self.workdir + '/day16/vexpress-v2p-ca9.dtb')
+ self.do_test_advcal_2018('16', tar_hash, 'winter.zImage')
+
+ def test_arm_ast2600_debian(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:rainier-bmc
+ """
+ deb_url = ('http://snapshot.debian.org/archive/debian/'
+ '20220606T211338Z/'
+ 'pool/main/l/linux/'
+ 'linux-image-5.17.0-2-armmp_5.17.6-1%2Bb1_armhf.deb')
+ deb_hash = '8acb2b4439faedc2f3ed4bdb2847ad4f6e0491f73debaeb7f660c8abe4dcdc0e'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash,
+ algorithm='sha256')
+ kernel_path = self.extract_from_deb(deb_path, '/boot/vmlinuz-5.17.0-2-armmp')
+ dtb_path = self.extract_from_deb(deb_path,
+ '/usr/lib/linux-image-5.17.0-2-armmp/aspeed-bmc-ibm-rainier.dtb')
+
+ self.vm.set_console()
+ self.vm.add_args('-kernel', kernel_path,
+ '-dtb', dtb_path,
+ '-net', 'nic')
+ self.vm.launch()
+ self.wait_for_console_pattern("Booting Linux on physical CPU 0xf00")
+ self.wait_for_console_pattern("SMP: Total of 2 processors activated")
+ self.wait_for_console_pattern("No filesystem could mount root")
+
+ def test_m68k_mcf5208evb(self):
+ """
+ :avocado: tags=arch:m68k
+ :avocado: tags=machine:mcf5208evb
+ """
+ tar_hash = 'ac688fd00561a2b6ce1359f9ff6aa2b98c9a570c'
+ self.do_test_advcal_2018('07', tar_hash, 'sanity-clause.elf')
+
+ def test_or1k_sim(self):
+ """
+ :avocado: tags=arch:or1k
+ :avocado: tags=machine:or1k-sim
+ """
+ tar_hash = '20334cdaf386108c530ff0badaecc955693027dd'
+ self.do_test_advcal_2018('20', tar_hash, 'vmlinux')
+
+ def test_ppc64_e500(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:ppce500
+ :avocado: tags=cpu:e5500
+ :avocado: tags=accel:tcg
+ """
+ self.require_accelerator("tcg")
+ tar_hash = '6951d86d644b302898da2fd701739c9406527fe1'
+ self.do_test_advcal_2018('19', tar_hash, 'uImage')
+
+ def do_test_ppc64_powernv(self, proc):
+ self.require_accelerator("tcg")
+ images_url = ('https://github.com/open-power/op-build/releases/download/v2.7/')
+
+ kernel_url = images_url + 'zImage.epapr'
+ kernel_hash = '0ab237df661727e5392cee97460e8674057a883c5f74381a128fa772588d45cd'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash,
+ algorithm='sha256')
+ self.vm.set_console()
+ self.vm.add_args('-kernel', kernel_path,
+ '-append', 'console=tty0 console=hvc0',
+ '-device', 'pcie-pci-bridge,id=bridge1,bus=pcie.1,addr=0x0',
+ '-device', 'nvme,bus=pcie.2,addr=0x0,serial=1234',
+ '-device', 'e1000e,bus=bridge1,addr=0x3',
+ '-device', 'nec-usb-xhci,bus=bridge1,addr=0x2')
+ self.vm.launch()
+
+ self.wait_for_console_pattern("CPU: " + proc + " generation processor")
+ self.wait_for_console_pattern("zImage starting: loaded")
+ self.wait_for_console_pattern("Run /init as init process")
+ # Device detection output driven by udev probing is sometimes cut off
+ # from console output, suspect S14silence-console init script.
+
+ def test_ppc_powernv8(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:powernv8
+ :avocado: tags=accel:tcg
+ """
+ self.do_test_ppc64_powernv('P8')
+
+ def test_ppc_powernv9(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:powernv9
+ :avocado: tags=accel:tcg
+ """
+ self.do_test_ppc64_powernv('P9')
+
+ def test_ppc_powernv10(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:powernv10
+ :avocado: tags=accel:tcg
+ """
+ self.do_test_ppc64_powernv('P10')
+
+ def test_ppc_g3beige(self):
+ """
+ :avocado: tags=arch:ppc
+ :avocado: tags=machine:g3beige
+ :avocado: tags=accel:tcg
+ """
+ # TODO: g3beige works with kvm_pr but we don't have a
+ # reliable way ATM (e.g. looking at /proc/modules) to detect
+ # whether we're running kvm_hv or kvm_pr. For now let's
+ # disable this test if we don't have TCG support.
+ self.require_accelerator("tcg")
+ tar_hash = 'e0b872a5eb8fdc5bed19bd43ffe863900ebcedfc'
+ self.vm.add_args('-M', 'graphics=off')
+ self.do_test_advcal_2018('15', tar_hash, 'invaders.elf')
+
+ def test_ppc_mac99(self):
+ """
+ :avocado: tags=arch:ppc
+ :avocado: tags=machine:mac99
+ :avocado: tags=accel:tcg
+ """
+ # TODO: mac99 works with kvm_pr but we don't have a
+ # reliable way ATM (e.g. looking at /proc/modules) to detect
+ # whether we're running kvm_hv or kvm_pr. For now let's
+ # disable this test if we don't have TCG support.
+ self.require_accelerator("tcg")
+ tar_hash = 'e0b872a5eb8fdc5bed19bd43ffe863900ebcedfc'
+ self.vm.add_args('-M', 'graphics=off')
+ self.do_test_advcal_2018('15', tar_hash, 'invaders.elf')
+
+ # This test has a 6-10% failure rate on various hosts that look
+ # like issues with a buggy kernel. As a result we don't want it
+ # gating releases on Gitlab.
+ @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab')
+
+ def test_sh4_r2d(self):
+ """
+ :avocado: tags=arch:sh4
+ :avocado: tags=machine:r2d
+ :avocado: tags=flaky
+ """
+ tar_hash = 'fe06a4fd8ccbf2e27928d64472939d47829d4c7e'
+ self.vm.add_args('-append', 'console=ttySC1')
+ self.do_test_advcal_2018('09', tar_hash, 'zImage', console=1)
+
+ def test_sparc_ss20(self):
+ """
+ :avocado: tags=arch:sparc
+ :avocado: tags=machine:SS-20
+ """
+ tar_hash = 'b18550d5d61c7615d989a06edace051017726a9f'
+ self.do_test_advcal_2018('11', tar_hash, 'zImage.elf')
+
+ def test_xtensa_lx60(self):
+ """
+ :avocado: tags=arch:xtensa
+ :avocado: tags=machine:lx60
+ :avocado: tags=cpu:dc233c
+ """
+ tar_hash = '49e88d9933742f0164b60839886c9739cb7a0d34'
+ self.do_test_advcal_2018('02', tar_hash, 'santas-sleigh-ride.elf')
diff --git a/tests/avocado/boot_xen.py b/tests/avocado/boot_xen.py
new file mode 100644
index 0000000000..fc2faeedb5
--- /dev/null
+++ b/tests/avocado/boot_xen.py
@@ -0,0 +1,116 @@
+# Functional test that boots a Xen hypervisor with a domU kernel and
+# checks the console output is vaguely sane .
+#
+# Copyright (c) 2020 Linaro
+#
+# Author:
+# Alex Bennée <alex.bennee@linaro.org>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import os
+
+from avocado_qemu import wait_for_console_pattern
+from boot_linux_console import LinuxKernelTest
+
+
+class BootXenBase(LinuxKernelTest):
+ """
+ Boots a Xen hypervisor with a Linux DomU kernel.
+ """
+
+ timeout = 90
+ XEN_COMMON_COMMAND_LINE = 'dom0_mem=128M loglvl=all guest_loglvl=all'
+
+ def fetch_guest_kernel(self):
+ # Using my own built kernel - which works
+ kernel_url = ('https://fileserver.linaro.org/'
+ 's/JSsewXGZ6mqxPr5/download?path=%2F&files='
+ 'linux-5.9.9-arm64-ajb')
+ kernel_sha1 = '4f92bc4b9f88d5ab792fa7a43a68555d344e1b83'
+ kernel_path = self.fetch_asset(kernel_url,
+ asset_hash=kernel_sha1)
+
+ return kernel_path
+
+ def launch_xen(self, xen_path):
+ """
+ Launch Xen with a dom0 guest kernel
+ """
+ self.log.info("launch with xen_path: %s", xen_path)
+ kernel_path = self.fetch_guest_kernel()
+
+ self.vm.set_console()
+
+ xen_command_line = self.XEN_COMMON_COMMAND_LINE
+ self.vm.add_args('-machine', 'virtualization=on',
+ '-m', '768',
+ '-kernel', xen_path,
+ '-append', xen_command_line,
+ '-device',
+ 'guest-loader,addr=0x47000000,kernel=%s,bootargs=console=hvc0'
+ % (kernel_path))
+
+ self.vm.launch()
+
+ console_pattern = 'VFS: Cannot open root device'
+ wait_for_console_pattern(self, console_pattern, "Panic on CPU 0:")
+
+
+class BootXen(BootXenBase):
+
+ def test_arm64_xen_411_and_dom0(self):
+ """
+ :avocado: tags=arch:aarch64
+ :avocado: tags=accel:tcg
+ :avocado: tags=cpu:cortex-a57
+ :avocado: tags=machine:virt
+ """
+
+ # archive of file from https://deb.debian.org/debian/pool/main/x/xen/
+ xen_url = ('https://fileserver.linaro.org/s/JSsewXGZ6mqxPr5/'
+ 'download?path=%2F&files='
+ 'xen-hypervisor-4.11-arm64_4.11.4%2B37-g3263f257ca-1_arm64.deb')
+ xen_sha1 = '034e634d4416adbad1212d59b62bccdcda63e62a'
+ xen_deb = self.fetch_asset(xen_url, asset_hash=xen_sha1)
+ xen_path = self.extract_from_deb(xen_deb, "/boot/xen-4.11-arm64")
+
+ self.launch_xen(xen_path)
+
+ def test_arm64_xen_414_and_dom0(self):
+ """
+ :avocado: tags=arch:aarch64
+ :avocado: tags=accel:tcg
+ :avocado: tags=cpu:cortex-a57
+ :avocado: tags=machine:virt
+ """
+
+ # archive of file from https://deb.debian.org/debian/pool/main/x/xen/
+ xen_url = ('https://fileserver.linaro.org/s/JSsewXGZ6mqxPr5/'
+ 'download?path=%2F&files='
+ 'xen-hypervisor-4.14-arm64_4.14.0%2B80-gd101b417b7-1_arm64.deb')
+ xen_sha1 = 'b9d209dd689ed2b393e625303a225badefec1160'
+ xen_deb = self.fetch_asset(xen_url, asset_hash=xen_sha1)
+ xen_path = self.extract_from_deb(xen_deb, "/boot/xen-4.14-arm64")
+
+ self.launch_xen(xen_path)
+
+ def test_arm64_xen_415_and_dom0(self):
+ """
+ :avocado: tags=arch:aarch64
+ :avocado: tags=accel:tcg
+ :avocado: tags=cpu:cortex-a57
+ :avocado: tags=machine:virt
+ """
+
+ xen_url = ('https://fileserver.linaro.org/'
+ 's/JSsewXGZ6mqxPr5/download'
+ '?path=%2F&files=xen-upstream-4.15-unstable.deb')
+ xen_sha1 = 'fc191172b85cf355abb95d275a24cc0f6d6579d8'
+ xen_deb = self.fetch_asset(xen_url, asset_hash=xen_sha1)
+ xen_path = self.extract_from_deb(xen_deb, "/boot/xen-4.15-unstable")
+
+ self.launch_xen(xen_path)
diff --git a/tests/avocado/cpu_queries.py b/tests/avocado/cpu_queries.py
new file mode 100644
index 0000000000..d3faa14720
--- /dev/null
+++ b/tests/avocado/cpu_queries.py
@@ -0,0 +1,35 @@
+# Sanity check of query-cpu-* results
+#
+# Copyright (c) 2019 Red Hat, Inc.
+#
+# Author:
+# Eduardo Habkost <ehabkost@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+from avocado_qemu import QemuSystemTest
+
+class QueryCPUModelExpansion(QemuSystemTest):
+ """
+ Run query-cpu-model-expansion for each CPU model, and validate results
+ """
+
+ def test(self):
+ """
+ :avocado: tags=arch:x86_64
+ :avocado: tags=machine:none
+ """
+ self.vm.add_args('-S')
+ self.vm.launch()
+
+ cpus = self.vm.cmd('query-cpu-definitions')
+ for c in cpus:
+ self.log.info("Checking CPU: %s", c)
+ self.assertNotIn('', c['unavailable-features'], c['name'])
+
+ for c in cpus:
+ model = {'name': c['name']}
+ e = self.vm.cmd('query-cpu-model-expansion', model=model,
+ type='full')
+ self.assertEqual(e['model']['name'], c['name'])
diff --git a/tests/avocado/empty_cpu_model.py b/tests/avocado/empty_cpu_model.py
new file mode 100644
index 0000000000..d906ef3d3c
--- /dev/null
+++ b/tests/avocado/empty_cpu_model.py
@@ -0,0 +1,19 @@
+# Check for crash when using empty -cpu option
+#
+# Copyright (c) 2019 Red Hat, Inc.
+#
+# Author:
+# Eduardo Habkost <ehabkost@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+from avocado_qemu import QemuSystemTest
+
+class EmptyCPUModel(QemuSystemTest):
+ def test(self):
+ self.vm.add_args('-S', '-display', 'none', '-machine', 'none', '-cpu', '')
+ self.vm.set_qmp_monitor(enabled=False)
+ self.vm.launch()
+ self.vm.wait()
+ self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
+ self.assertRegex(self.vm.get_log(), r'-cpu option cannot be empty')
diff --git a/tests/avocado/hotplug_cpu.py b/tests/avocado/hotplug_cpu.py
new file mode 100644
index 0000000000..292bb43e4d
--- /dev/null
+++ b/tests/avocado/hotplug_cpu.py
@@ -0,0 +1,37 @@
+# Functional test that hotplugs a CPU and checks it on a Linux guest
+#
+# Copyright (c) 2021 Red Hat, Inc.
+#
+# Author:
+# Cleber Rosa <crosa@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+from avocado_qemu import LinuxTest
+
+
+class HotPlugCPU(LinuxTest):
+
+ def test(self):
+ """
+ :avocado: tags=arch:x86_64
+ :avocado: tags=machine:q35
+ :avocado: tags=accel:kvm
+ """
+ self.require_accelerator('kvm')
+ self.vm.add_args('-accel', 'kvm')
+ self.vm.add_args('-cpu', 'Haswell')
+ self.vm.add_args('-smp', '1,sockets=1,cores=2,threads=1,maxcpus=2')
+ self.launch_and_wait()
+
+ self.ssh_command('test -e /sys/devices/system/cpu/cpu0')
+ with self.assertRaises(AssertionError):
+ self.ssh_command('test -e /sys/devices/system/cpu/cpu1')
+
+ self.vm.cmd('device_add',
+ driver='Haswell-x86_64-cpu',
+ socket_id=0,
+ core_id=1,
+ thread_id=0)
+ self.ssh_command('test -e /sys/devices/system/cpu/cpu1')
diff --git a/tests/avocado/info_usernet.py b/tests/avocado/info_usernet.py
new file mode 100644
index 0000000000..e1aa7a6e0a
--- /dev/null
+++ b/tests/avocado/info_usernet.py
@@ -0,0 +1,33 @@
+# Test for the hmp command "info usernet"
+#
+# Copyright (c) 2021 Red Hat, Inc.
+#
+# Author:
+# Cleber Rosa <crosa@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+from avocado_qemu import QemuSystemTest
+
+from qemu.utils import get_info_usernet_hostfwd_port
+
+
+class InfoUsernet(QemuSystemTest):
+ """
+ :avocado: tags=machine:none
+ """
+
+ def test_hostfwd(self):
+ self.require_netdev('user')
+ self.vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22')
+ self.vm.launch()
+ res = self.vm.cmd('human-monitor-command',
+ command_line='info usernet')
+ port = get_info_usernet_hostfwd_port(res)
+ self.assertIsNotNone(port,
+ ('"info usernet" output content does not seem to '
+ 'contain the redirected port'))
+ self.assertGreater(port, 0,
+ ('Found a redirected port that is not greater than'
+ ' zero'))
diff --git a/tests/avocado/intel_iommu.py b/tests/avocado/intel_iommu.py
new file mode 100644
index 0000000000..f04ee1cf9d
--- /dev/null
+++ b/tests/avocado/intel_iommu.py
@@ -0,0 +1,123 @@
+# INTEL_IOMMU Functional tests
+#
+# Copyright (c) 2021 Red Hat, Inc.
+#
+# Author:
+# Eric Auger <eric.auger@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+import os
+
+from avocado import skipUnless
+from avocado_qemu import LinuxTest
+
+@skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab')
+
+class IntelIOMMU(LinuxTest):
+ """
+ :avocado: tags=arch:x86_64
+ :avocado: tags=distro:fedora
+ :avocado: tags=distro_version:31
+ :avocado: tags=machine:q35
+ :avocado: tags=accel:kvm
+ :avocado: tags=intel_iommu
+ :avocado: tags=flaky
+ """
+
+ IOMMU_ADDON = ',iommu_platform=on,disable-modern=off,disable-legacy=on'
+ kernel_path = None
+ initrd_path = None
+ kernel_params = None
+
+ def set_up_boot(self):
+ path = self.download_boot()
+ self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,scsi=off,' +
+ 'drive=drv0,id=virtio-disk0,bootindex=1,'
+ 'werror=stop,rerror=stop' + self.IOMMU_ADDON)
+ self.vm.add_args('-device', 'virtio-gpu-pci' + self.IOMMU_ADDON)
+ self.vm.add_args('-drive',
+ 'file=%s,if=none,cache=writethrough,id=drv0' % path)
+
+ def setUp(self):
+ super(IntelIOMMU, self).setUp(None, 'virtio-net-pci' + self.IOMMU_ADDON)
+
+ def add_common_args(self):
+ self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0')
+ self.vm.add_args('-object',
+ 'rng-random,id=rng0,filename=/dev/urandom')
+
+ def common_vm_setup(self, custom_kernel=None):
+ self.require_accelerator("kvm")
+ self.add_common_args()
+ self.vm.add_args("-accel", "kvm")
+
+ if custom_kernel is None:
+ return
+
+ kernel_url = self.distro.pxeboot_url + 'vmlinuz'
+ kernel_hash = '5b6f6876e1b5bda314f93893271da0d5777b1f3c'
+ initrd_url = self.distro.pxeboot_url + 'initrd.img'
+ initrd_hash = 'dd0340a1b39bd28f88532babd4581c67649ec5b1'
+ self.kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+ self.initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
+
+ def run_and_check(self):
+ if self.kernel_path:
+ self.vm.add_args('-kernel', self.kernel_path,
+ '-append', self.kernel_params,
+ '-initrd', self.initrd_path)
+ self.launch_and_wait()
+ self.ssh_command('cat /proc/cmdline')
+ self.ssh_command('dmesg | grep -e DMAR -e IOMMU')
+ self.ssh_command('find /sys/kernel/iommu_groups/ -type l')
+ self.ssh_command('dnf -y install numactl-devel')
+
+ def test_intel_iommu(self):
+ """
+ :avocado: tags=intel_iommu_intremap
+ """
+
+ self.common_vm_setup(True)
+ self.vm.add_args('-device', 'intel-iommu,intremap=on')
+ self.vm.add_args('-machine', 'kernel_irqchip=split')
+
+ self.kernel_params = (self.distro.default_kernel_params +
+ ' quiet intel_iommu=on')
+ self.run_and_check()
+
+ def test_intel_iommu_strict(self):
+ """
+ :avocado: tags=intel_iommu_strict
+ """
+
+ self.common_vm_setup(True)
+ self.vm.add_args('-device', 'intel-iommu,intremap=on')
+ self.vm.add_args('-machine', 'kernel_irqchip=split')
+ self.kernel_params = (self.distro.default_kernel_params +
+ ' quiet intel_iommu=on,strict')
+ self.run_and_check()
+
+ def test_intel_iommu_strict_cm(self):
+ """
+ :avocado: tags=intel_iommu_strict_cm
+ """
+
+ self.common_vm_setup(True)
+ self.vm.add_args('-device', 'intel-iommu,intremap=on,caching-mode=on')
+ self.vm.add_args('-machine', 'kernel_irqchip=split')
+ self.kernel_params = (self.distro.default_kernel_params +
+ ' quiet intel_iommu=on,strict')
+ self.run_and_check()
+
+ def test_intel_iommu_pt(self):
+ """
+ :avocado: tags=intel_iommu_pt
+ """
+
+ self.common_vm_setup(True)
+ self.vm.add_args('-device', 'intel-iommu,intremap=on')
+ self.vm.add_args('-machine', 'kernel_irqchip=split')
+ self.kernel_params = (self.distro.default_kernel_params +
+ ' quiet intel_iommu=on iommu=pt')
+ self.run_and_check()
diff --git a/tests/avocado/kvm_xen_guest.py b/tests/avocado/kvm_xen_guest.py
new file mode 100644
index 0000000000..f8cb458d5d
--- /dev/null
+++ b/tests/avocado/kvm_xen_guest.py
@@ -0,0 +1,171 @@
+# KVM Xen guest functional tests
+#
+# Copyright © 2021 Red Hat, Inc.
+# Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Author:
+# David Woodhouse <dwmw2@infradead.org>
+# Alex Bennée <alex.bennee@linaro.org>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import os
+
+from qemu.machine import machine
+
+from avocado_qemu import LinuxSSHMixIn
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+
+class KVMXenGuest(QemuSystemTest, LinuxSSHMixIn):
+ """
+ :avocado: tags=arch:x86_64
+ :avocado: tags=machine:q35
+ :avocado: tags=accel:kvm
+ :avocado: tags=kvm_xen_guest
+ """
+
+ KERNEL_DEFAULT = 'printk.time=0 root=/dev/xvda console=ttyS0'
+
+ kernel_path = None
+ kernel_params = None
+
+ # Fetch assets from the kvm-xen-guest subdir of my shared test
+ # images directory on fileserver.linaro.org where you can find
+ # build instructions for how they where assembled.
+ def get_asset(self, name, sha1):
+ base_url = ('https://fileserver.linaro.org/s/'
+ 'kE4nCFLdQcoBF9t/download?'
+ 'path=%2Fkvm-xen-guest&files=' )
+ url = base_url + name
+ # use explicit name rather than failing to neatly parse the
+ # URL into a unique one
+ return self.fetch_asset(name=name, locations=(url), asset_hash=sha1)
+
+ def common_vm_setup(self):
+ # We also catch lack of KVM_XEN support if we fail to launch
+ self.require_accelerator("kvm")
+
+ self.vm.set_console()
+
+ self.vm.add_args("-accel", "kvm,xen-version=0x4000a,kernel-irqchip=split")
+ self.vm.add_args("-smp", "2")
+
+ self.kernel_path = self.get_asset("bzImage",
+ "367962983d0d32109998a70b45dcee4672d0b045")
+ self.rootfs = self.get_asset("rootfs.ext4",
+ "f1478401ea4b3fa2ea196396be44315bab2bb5e4")
+
+ def run_and_check(self):
+ self.vm.add_args('-kernel', self.kernel_path,
+ '-append', self.kernel_params,
+ '-drive', f"file={self.rootfs},if=none,snapshot=on,format=raw,id=drv0",
+ '-device', 'xen-disk,drive=drv0,vdev=xvda',
+ '-device', 'virtio-net-pci,netdev=unet',
+ '-netdev', 'user,id=unet,hostfwd=:127.0.0.1:0-:22')
+
+ try:
+ self.vm.launch()
+ except machine.VMLaunchFailure as e:
+ if "Xen HVM guest support not present" in e.output:
+ self.cancel("KVM Xen support is not present "
+ "(need v5.12+ kernel with CONFIG_KVM_XEN)")
+ elif "Property 'kvm-accel.xen-version' not found" in e.output:
+ self.cancel("QEMU not built with CONFIG_XEN_EMU support")
+ else:
+ raise e
+
+ self.log.info('VM launched, waiting for sshd')
+ console_pattern = 'Starting dropbear sshd: OK'
+ wait_for_console_pattern(self, console_pattern, 'Oops')
+ self.log.info('sshd ready')
+ self.ssh_connect('root', '', False)
+
+ self.ssh_command('cat /proc/cmdline')
+ self.ssh_command('dmesg | grep -e "Grant table initialized"')
+
+ def test_kvm_xen_guest(self):
+ """
+ :avocado: tags=kvm_xen_guest
+ """
+
+ self.common_vm_setup()
+
+ self.kernel_params = (self.KERNEL_DEFAULT +
+ ' xen_emul_unplug=ide-disks')
+ self.run_and_check()
+ self.ssh_command('grep xen-pirq.*msi /proc/interrupts')
+
+ def test_kvm_xen_guest_nomsi(self):
+ """
+ :avocado: tags=kvm_xen_guest_nomsi
+ """
+
+ self.common_vm_setup()
+
+ self.kernel_params = (self.KERNEL_DEFAULT +
+ ' xen_emul_unplug=ide-disks pci=nomsi')
+ self.run_and_check()
+ self.ssh_command('grep xen-pirq.* /proc/interrupts')
+
+ def test_kvm_xen_guest_noapic_nomsi(self):
+ """
+ :avocado: tags=kvm_xen_guest_noapic_nomsi
+ """
+
+ self.common_vm_setup()
+
+ self.kernel_params = (self.KERNEL_DEFAULT +
+ ' xen_emul_unplug=ide-disks noapic pci=nomsi')
+ self.run_and_check()
+ self.ssh_command('grep xen-pirq /proc/interrupts')
+
+ def test_kvm_xen_guest_vapic(self):
+ """
+ :avocado: tags=kvm_xen_guest_vapic
+ """
+
+ self.common_vm_setup()
+ self.vm.add_args('-cpu', 'host,+xen-vapic')
+ self.kernel_params = (self.KERNEL_DEFAULT +
+ ' xen_emul_unplug=ide-disks')
+ self.run_and_check()
+ self.ssh_command('grep xen-pirq /proc/interrupts')
+ self.ssh_command('grep PCI-MSI /proc/interrupts')
+
+ def test_kvm_xen_guest_novector(self):
+ """
+ :avocado: tags=kvm_xen_guest_novector
+ """
+
+ self.common_vm_setup()
+ self.kernel_params = (self.KERNEL_DEFAULT +
+ ' xen_emul_unplug=ide-disks' +
+ ' xen_no_vector_callback')
+ self.run_and_check()
+ self.ssh_command('grep xen-platform-pci /proc/interrupts')
+
+ def test_kvm_xen_guest_novector_nomsi(self):
+ """
+ :avocado: tags=kvm_xen_guest_novector_nomsi
+ """
+
+ self.common_vm_setup()
+
+ self.kernel_params = (self.KERNEL_DEFAULT +
+ ' xen_emul_unplug=ide-disks pci=nomsi' +
+ ' xen_no_vector_callback')
+ self.run_and_check()
+ self.ssh_command('grep xen-platform-pci /proc/interrupts')
+
+ def test_kvm_xen_guest_novector_noapic(self):
+ """
+ :avocado: tags=kvm_xen_guest_novector_noapic
+ """
+
+ self.common_vm_setup()
+ self.kernel_params = (self.KERNEL_DEFAULT +
+ ' xen_emul_unplug=ide-disks' +
+ ' xen_no_vector_callback noapic')
+ self.run_and_check()
+ self.ssh_command('grep xen-platform-pci /proc/interrupts')
diff --git a/tests/avocado/linux_initrd.py b/tests/avocado/linux_initrd.py
new file mode 100644
index 0000000000..aad5b19bd9
--- /dev/null
+++ b/tests/avocado/linux_initrd.py
@@ -0,0 +1,92 @@
+# Linux initrd integration test.
+#
+# Copyright (c) 2018 Red Hat, Inc.
+#
+# Author:
+# Wainer dos Santos Moschetta <wainersm@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import os
+import logging
+import tempfile
+
+from avocado_qemu import QemuSystemTest
+from avocado import skipUnless
+
+
+class LinuxInitrd(QemuSystemTest):
+ """
+ Checks QEMU evaluates correctly the initrd file passed as -initrd option.
+
+ :avocado: tags=arch:x86_64
+ :avocado: tags=machine:pc
+ """
+
+ timeout = 300
+
+ def test_with_2gib_file_should_exit_error_msg_with_linux_v3_6(self):
+ """
+ Pretends to boot QEMU with an initrd file with size of 2GiB
+ and expect it exits with error message.
+ Fedora-18 shipped with linux-3.6 which have not supported xloadflags
+ cannot support more than 2GiB initrd.
+ """
+ kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora/li'
+ 'nux/releases/18/Fedora/x86_64/os/images/pxeboot/vmlinuz')
+ kernel_hash = '41464f68efe42b9991250bed86c7081d2ccdbb21'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+ max_size = 2 * (1024 ** 3) - 1
+
+ with tempfile.NamedTemporaryFile() as initrd:
+ initrd.seek(max_size)
+ initrd.write(b'\0')
+ initrd.flush()
+ self.vm.add_args('-kernel', kernel_path, '-initrd', initrd.name,
+ '-m', '4096')
+ self.vm.set_qmp_monitor(enabled=False)
+ self.vm.launch()
+ self.vm.wait()
+ self.assertEqual(self.vm.exitcode(), 1)
+ expected_msg = r'.*initrd is too large.*max: \d+, need %s.*' % (
+ max_size + 1)
+ self.assertRegex(self.vm.get_log(), expected_msg)
+
+ @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab')
+
+ def test_with_2gib_file_should_work_with_linux_v4_16(self):
+ """
+ :avocado: tags=flaky
+
+ QEMU has supported up to 4 GiB initrd for recent kernel
+ Expect guest can reach 'Unpacking initramfs...'
+ """
+ kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora'
+ '/linux/releases/28/Everything/x86_64/os/images/pxeboot/'
+ 'vmlinuz')
+ kernel_hash = '238e083e114c48200f80d889f7e32eeb2793e02a'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+ max_size = 2 * (1024 ** 3) + 1
+
+ with tempfile.NamedTemporaryFile() as initrd:
+ initrd.seek(max_size)
+ initrd.write(b'\0')
+ initrd.flush()
+
+ self.vm.set_console()
+ kernel_command_line = 'console=ttyS0'
+ self.vm.add_args('-kernel', kernel_path,
+ '-append', kernel_command_line,
+ '-initrd', initrd.name,
+ '-m', '5120')
+ self.vm.launch()
+ console = self.vm.console_socket.makefile()
+ console_logger = logging.getLogger('console')
+ while True:
+ msg = console.readline()
+ console_logger.debug(msg.strip())
+ if 'Unpacking initramfs...' in msg:
+ break
+ if 'Kernel panic - not syncing' in msg:
+ self.fail("Kernel panic reached")
diff --git a/tests/avocado/linux_ssh_mips_malta.py b/tests/avocado/linux_ssh_mips_malta.py
new file mode 100644
index 0000000000..d9bb525ad9
--- /dev/null
+++ b/tests/avocado/linux_ssh_mips_malta.py
@@ -0,0 +1,205 @@
+# Functional test that boots a VM and run commands via a SSH session
+#
+# Copyright (c) Philippe Mathieu-Daudé <f4bug@amsat.org>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import os
+import re
+import base64
+import logging
+import time
+
+from avocado import skipUnless
+from avocado_qemu import LinuxSSHMixIn
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+from avocado.utils import process
+from avocado.utils import archive
+from avocado.utils import ssh
+
+
+@skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout')
+@skipUnless(ssh.SSH_CLIENT_BINARY, 'No SSH client available')
+class LinuxSSH(QemuSystemTest, LinuxSSHMixIn):
+ """
+ :avocado: tags=accel:tcg
+ """
+
+ timeout = 150 # Not for 'configure --enable-debug --enable-debug-tcg'
+
+ KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
+ VM_IP = '127.0.0.1'
+
+ BASE_URL = 'https://people.debian.org/~aurel32/qemu/'
+ IMAGE_INFO = {
+ 'be': {'base_url': 'mips',
+ 'image_name': 'debian_wheezy_mips_standard.qcow2',
+ 'image_hash': '8987a63270df67345b2135a6b7a4885a35e392d5',
+ 'kernel_hash': {
+ 32: '592e384a4edc16dade52a6cd5c785c637bcbc9ad',
+ 64: 'db6eea7de35d36c77d8c165b6bcb222e16eb91db'}
+ },
+ 'le': {'base_url': 'mipsel',
+ 'image_name': 'debian_wheezy_mipsel_standard.qcow2',
+ 'image_hash': '7866764d9de3ef536ffca24c9fb9f04ffdb45802',
+ 'kernel_hash': {
+ 32: 'a66bea5a8adaa2cb3d36a1d4e0ccdb01be8f6c2a',
+ 64: '6a7f77245acf231415a0e8b725d91ed2f3487794'}
+ }
+ }
+ CPU_INFO = {
+ 32: {'cpu': 'MIPS 24Kc', 'kernel_release': '3.2.0-4-4kc-malta'},
+ 64: {'cpu': 'MIPS 20Kc', 'kernel_release': '3.2.0-4-5kc-malta'}
+ }
+
+ def get_url(self, endianess, path=''):
+ qkey = {'le': 'el', 'be': ''}
+ return '%s/mips%s/%s' % (self.BASE_URL, qkey[endianess], path)
+
+ def get_image_info(self, endianess):
+ dinfo = self.IMAGE_INFO[endianess]
+ image_url = self.get_url(endianess, dinfo['image_name'])
+ image_hash = dinfo['image_hash']
+ return (image_url, image_hash)
+
+ def get_kernel_info(self, endianess, wordsize):
+ minfo = self.CPU_INFO[wordsize]
+ kernel_url = self.get_url(endianess,
+ 'vmlinux-%s' % minfo['kernel_release'])
+ kernel_hash = self.IMAGE_INFO[endianess]['kernel_hash'][wordsize]
+ return kernel_url, kernel_hash
+
+ def ssh_disconnect_vm(self):
+ self.ssh_session.quit()
+
+ def boot_debian_wheezy_image_and_ssh_login(self, endianess, kernel_path):
+ image_url, image_hash = self.get_image_info(endianess)
+ image_path = self.fetch_asset(image_url, asset_hash=image_hash)
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE
+ + 'console=ttyS0 root=/dev/sda1')
+ self.vm.add_args('-no-reboot',
+ '-kernel', kernel_path,
+ '-append', kernel_command_line,
+ '-drive', 'file=%s,snapshot=on' % image_path,
+ '-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22',
+ '-device', 'pcnet,netdev=vnet')
+ self.vm.launch()
+
+ self.log.info('VM launched, waiting for sshd')
+ console_pattern = 'Starting OpenBSD Secure Shell server: sshd'
+ wait_for_console_pattern(self, console_pattern, 'Oops')
+ self.log.info('sshd ready')
+
+ self.ssh_connect('root', 'root', False)
+
+ def shutdown_via_ssh(self):
+ self.ssh_command('poweroff')
+ self.ssh_disconnect_vm()
+ wait_for_console_pattern(self, 'Power down', 'Oops')
+
+ def run_common_commands(self, wordsize):
+ self.ssh_command_output_contains(
+ 'cat /proc/cpuinfo',
+ self.CPU_INFO[wordsize]['cpu'])
+ self.ssh_command_output_contains(
+ 'uname -m',
+ 'mips')
+ self.ssh_command_output_contains(
+ 'uname -r',
+ self.CPU_INFO[wordsize]['kernel_release'])
+ self.ssh_command_output_contains(
+ 'cat /proc/interrupts',
+ 'XT-PIC timer')
+ self.ssh_command_output_contains(
+ 'cat /proc/interrupts',
+ 'XT-PIC i8042')
+ self.ssh_command_output_contains(
+ 'cat /proc/interrupts',
+ 'XT-PIC serial')
+ self.ssh_command_output_contains(
+ 'cat /proc/interrupts',
+ 'XT-PIC ata_piix')
+ self.ssh_command_output_contains(
+ 'cat /proc/interrupts',
+ 'XT-PIC eth0')
+ self.ssh_command_output_contains(
+ 'cat /proc/devices',
+ 'input')
+ self.ssh_command_output_contains(
+ 'cat /proc/devices',
+ 'usb')
+ self.ssh_command_output_contains(
+ 'cat /proc/devices',
+ 'fb')
+ self.ssh_command_output_contains(
+ 'cat /proc/ioports',
+ ' : serial')
+ self.ssh_command_output_contains(
+ 'cat /proc/ioports',
+ ' : ata_piix')
+ self.ssh_command_output_contains(
+ 'cat /proc/ioports',
+ ' : piix4_smbus')
+ self.ssh_command_output_contains(
+ 'lspci -d 11ab:4620',
+ 'GT-64120')
+ self.ssh_command_output_contains(
+ 'cat /sys/bus/i2c/devices/i2c-0/name',
+ 'SMBus PIIX4 adapter')
+ self.ssh_command_output_contains(
+ 'cat /proc/mtd',
+ 'YAMON')
+ # Empty 'Board Config' (64KB)
+ self.ssh_command_output_contains(
+ 'md5sum /dev/mtd2ro',
+ '0dfbe8aa4c20b52e1b8bf3cb6cbdf193')
+
+ def check_mips_malta(self, uname_m, endianess):
+ wordsize = 64 if '64' in uname_m else 32
+ kernel_url, kernel_hash = self.get_kernel_info(endianess, wordsize)
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+ self.boot_debian_wheezy_image_and_ssh_login(endianess, kernel_path)
+
+ stdout, _ = self.ssh_command('uname -a')
+ self.assertIn(True, [uname_m + " GNU/Linux" in line for line in stdout])
+
+ self.run_common_commands(wordsize)
+ self.shutdown_via_ssh()
+ # Wait for VM to shut down gracefully
+ self.vm.wait()
+
+ def test_mips_malta32eb_kernel3_2_0(self):
+ """
+ :avocado: tags=arch:mips
+ :avocado: tags=endian:big
+ :avocado: tags=device:pcnet32
+ """
+ self.check_mips_malta('mips', 'be')
+
+ def test_mips_malta32el_kernel3_2_0(self):
+ """
+ :avocado: tags=arch:mipsel
+ :avocado: tags=endian:little
+ :avocado: tags=device:pcnet32
+ """
+ self.check_mips_malta('mips', 'le')
+
+ def test_mips_malta64eb_kernel3_2_0(self):
+ """
+ :avocado: tags=arch:mips64
+ :avocado: tags=endian:big
+ :avocado: tags=device:pcnet32
+ """
+ self.check_mips_malta('mips64', 'be')
+
+ def test_mips_malta64el_kernel3_2_0(self):
+ """
+ :avocado: tags=arch:mips64el
+ :avocado: tags=endian:little
+ :avocado: tags=device:pcnet32
+ """
+ self.check_mips_malta('mips64', 'le')
diff --git a/tests/avocado/load_bflt.py b/tests/avocado/load_bflt.py
new file mode 100644
index 0000000000..bb50cec1ee
--- /dev/null
+++ b/tests/avocado/load_bflt.py
@@ -0,0 +1,54 @@
+# Test the bFLT loader format
+#
+# Copyright (C) 2019 Philippe Mathieu-Daudé <f4bug@amsat.org>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import os
+import bz2
+import subprocess
+
+from avocado import skipUnless
+from avocado_qemu import QemuUserTest
+from avocado_qemu import has_cmd
+
+
+class LoadBFLT(QemuUserTest):
+
+ def extract_cpio(self, cpio_path):
+ """
+ Extracts a cpio archive into the test workdir
+
+ :param cpio_path: path to the cpio archive
+ """
+ cwd = os.getcwd()
+ os.chdir(self.workdir)
+ with bz2.open(cpio_path, 'rb') as archive_cpio:
+ subprocess.run(['cpio', '-i'], input=archive_cpio.read(),
+ stderr=subprocess.DEVNULL)
+ os.chdir(cwd)
+
+ @skipUnless(*has_cmd('cpio'))
+ @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code')
+ def test_stm32(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=linux_user
+ :avocado: tags=quick
+ """
+ # See https://elinux.org/STM32#User_Space
+ rootfs_url = ('https://elinux.org/images/5/51/'
+ 'Stm32_mini_rootfs.cpio.bz2')
+ rootfs_hash = '9f065e6ba40cce7411ba757f924f30fcc57951e6'
+ rootfs_path_bz2 = self.fetch_asset(rootfs_url, asset_hash=rootfs_hash)
+ busybox_path = os.path.join(self.workdir, "/bin/busybox")
+
+ self.extract_cpio(rootfs_path_bz2)
+
+ res = self.run(busybox_path)
+ ver = 'BusyBox v1.24.0.git (2015-02-03 22:17:13 CET) multi-call binary.'
+ self.assertIn(ver, res.stdout_text)
+
+ res = self.run(busybox_path, ['uname', '-a'])
+ unm = 'armv7l GNU/Linux'
+ self.assertIn(unm, res.stdout_text)
diff --git a/tests/avocado/machine_aarch64_sbsaref.py b/tests/avocado/machine_aarch64_sbsaref.py
new file mode 100644
index 0000000000..98c76c1ff7
--- /dev/null
+++ b/tests/avocado/machine_aarch64_sbsaref.py
@@ -0,0 +1,238 @@
+# Functional test that boots a Linux kernel and checks the console
+#
+# SPDX-FileCopyrightText: 2023-2024 Linaro Ltd.
+# SPDX-FileContributor: Philippe Mathieu-Daudé <philmd@linaro.org>
+# SPDX-FileContributor: Marcin Juszkiewicz <marcin.juszkiewicz@linaro.org>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import os
+
+from avocado import skipUnless
+from avocado.utils import archive
+
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+from avocado_qemu import interrupt_interactive_console_until_pattern
+
+
+class Aarch64SbsarefMachine(QemuSystemTest):
+ """
+ :avocado: tags=arch:aarch64
+ :avocado: tags=machine:sbsa-ref
+ :avocado: tags=accel:tcg
+
+ As firmware runs at a higher privilege level than the hypervisor we
+ can only run these tests under TCG emulation.
+ """
+
+ timeout = 180
+
+ def fetch_firmware(self):
+ """
+ Flash volumes generated using:
+
+ Toolchain from Debian:
+ aarch64-linux-gnu-gcc (Debian 12.2.0-14) 12.2.0
+
+ Used components:
+
+ - Trusted Firmware 2.10.2
+ - Tianocore EDK2 stable202402
+ - Tianocore EDK2-platforms commit 085c2fb
+
+ """
+
+ # Secure BootRom (TF-A code)
+ fs0_xz_url = (
+ "https://artifacts.codelinaro.org/artifactory/linaro-419-sbsa-ref/"
+ "20240313-116475/edk2/SBSA_FLASH0.fd.xz"
+ )
+ fs0_xz_hash = "637593749cc307dea7dc13265c32e5d020267552f22b18a31850b8429fc5e159"
+ tar_xz_path = self.fetch_asset(fs0_xz_url, asset_hash=fs0_xz_hash,
+ algorithm='sha256')
+ archive.extract(tar_xz_path, self.workdir)
+ fs0_path = os.path.join(self.workdir, "SBSA_FLASH0.fd")
+
+ # Non-secure rom (UEFI and EFI variables)
+ fs1_xz_url = (
+ "https://artifacts.codelinaro.org/artifactory/linaro-419-sbsa-ref/"
+ "20240313-116475/edk2/SBSA_FLASH1.fd.xz"
+ )
+ fs1_xz_hash = "cb0a5e8cf5e303c5d3dc106cfd5943ffe9714b86afddee7164c69ee1dd41991c"
+ tar_xz_path = self.fetch_asset(fs1_xz_url, asset_hash=fs1_xz_hash,
+ algorithm='sha256')
+ archive.extract(tar_xz_path, self.workdir)
+ fs1_path = os.path.join(self.workdir, "SBSA_FLASH1.fd")
+
+ for path in [fs0_path, fs1_path]:
+ with open(path, "ab+") as fd:
+ fd.truncate(256 << 20) # Expand volumes to 256MiB
+
+ self.vm.set_console()
+ self.vm.add_args(
+ "-drive",
+ f"if=pflash,file={fs0_path},format=raw",
+ "-drive",
+ f"if=pflash,file={fs1_path},format=raw",
+ "-smp",
+ "1",
+ "-machine",
+ "sbsa-ref",
+ )
+
+ def test_sbsaref_edk2_firmware(self):
+ """
+ :avocado: tags=cpu:cortex-a57
+ """
+
+ self.fetch_firmware()
+ self.vm.launch()
+
+ # TF-A boot sequence:
+ #
+ # https://github.com/ARM-software/arm-trusted-firmware/blob/v2.8.0/\
+ # docs/design/trusted-board-boot.rst#trusted-board-boot-sequence
+ # https://trustedfirmware-a.readthedocs.io/en/v2.8/\
+ # design/firmware-design.html#cold-boot
+
+ # AP Trusted ROM
+ wait_for_console_pattern(self, "Booting Trusted Firmware")
+ wait_for_console_pattern(self, "BL1: v2.10.2(release):")
+ wait_for_console_pattern(self, "BL1: Booting BL2")
+
+ # Trusted Boot Firmware
+ wait_for_console_pattern(self, "BL2: v2.10.2(release)")
+ wait_for_console_pattern(self, "Booting BL31")
+
+ # EL3 Runtime Software
+ wait_for_console_pattern(self, "BL31: v2.10.2(release)")
+
+ # Non-trusted Firmware
+ wait_for_console_pattern(self, "UEFI firmware (version 1.0")
+ interrupt_interactive_console_until_pattern(self, "QEMU SBSA-REF Machine")
+
+ # This tests the whole boot chain from EFI to Userspace
+ # We only boot a whole OS for the current top level CPU and GIC
+ # Other test profiles should use more minimal boots
+ def boot_alpine_linux(self, cpu):
+ self.fetch_firmware()
+
+ iso_url = (
+ "https://dl-cdn.alpinelinux.org/"
+ "alpine/v3.17/releases/aarch64/alpine-standard-3.17.2-aarch64.iso"
+ )
+
+ iso_hash = "5a36304ecf039292082d92b48152a9ec21009d3a62f459de623e19c4bd9dc027"
+ iso_path = self.fetch_asset(iso_url, algorithm="sha256", asset_hash=iso_hash)
+
+ self.vm.set_console()
+ self.vm.add_args(
+ "-cpu",
+ cpu,
+ "-drive",
+ f"file={iso_path},format=raw",
+ )
+
+ self.vm.launch()
+ wait_for_console_pattern(self, "Welcome to Alpine Linux 3.17")
+
+ def test_sbsaref_alpine_linux_cortex_a57(self):
+ """
+ :avocado: tags=cpu:cortex-a57
+ :avocado: tags=os:linux
+ """
+ self.boot_alpine_linux("cortex-a57")
+
+ def test_sbsaref_alpine_linux_neoverse_n1(self):
+ """
+ :avocado: tags=cpu:neoverse-n1
+ :avocado: tags=os:linux
+ """
+ self.boot_alpine_linux("neoverse-n1")
+
+ def test_sbsaref_alpine_linux_max_pauth_off(self):
+ """
+ :avocado: tags=cpu:max
+ :avocado: tags=os:linux
+ """
+ self.boot_alpine_linux("max,pauth=off")
+
+ def test_sbsaref_alpine_linux_max_pauth_impdef(self):
+ """
+ :avocado: tags=cpu:max
+ :avocado: tags=os:linux
+ """
+ self.boot_alpine_linux("max,pauth-impdef=on")
+
+ @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout')
+ def test_sbsaref_alpine_linux_max(self):
+ """
+ :avocado: tags=cpu:max
+ :avocado: tags=os:linux
+ """
+ self.boot_alpine_linux("max")
+
+
+ # This tests the whole boot chain from EFI to Userspace
+ # We only boot a whole OS for the current top level CPU and GIC
+ # Other test profiles should use more minimal boots
+ def boot_openbsd73(self, cpu):
+ self.fetch_firmware()
+
+ img_url = (
+ "https://cdn.openbsd.org/pub/OpenBSD/7.3/arm64/miniroot73.img"
+ )
+
+ img_hash = "7fc2c75401d6f01fbfa25f4953f72ad7d7c18650056d30755c44b9c129b707e5"
+ img_path = self.fetch_asset(img_url, algorithm="sha256", asset_hash=img_hash)
+
+ self.vm.set_console()
+ self.vm.add_args(
+ "-cpu",
+ cpu,
+ "-drive",
+ f"file={img_path},format=raw",
+ )
+
+ self.vm.launch()
+ wait_for_console_pattern(self,
+ "Welcome to the OpenBSD/arm64"
+ " 7.3 installation program.")
+
+ def test_sbsaref_openbsd73_cortex_a57(self):
+ """
+ :avocado: tags=cpu:cortex-a57
+ :avocado: tags=os:openbsd
+ """
+ self.boot_openbsd73("cortex-a57")
+
+ def test_sbsaref_openbsd73_neoverse_n1(self):
+ """
+ :avocado: tags=cpu:neoverse-n1
+ :avocado: tags=os:openbsd
+ """
+ self.boot_openbsd73("neoverse-n1")
+
+ def test_sbsaref_openbsd73_max_pauth_off(self):
+ """
+ :avocado: tags=cpu:max
+ :avocado: tags=os:openbsd
+ """
+ self.boot_openbsd73("max,pauth=off")
+
+ @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout')
+ def test_sbsaref_openbsd73_max_pauth_impdef(self):
+ """
+ :avocado: tags=cpu:max
+ :avocado: tags=os:openbsd
+ """
+ self.boot_openbsd73("max,pauth-impdef=on")
+
+ @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout')
+ def test_sbsaref_openbsd73_max(self):
+ """
+ :avocado: tags=cpu:max
+ :avocado: tags=os:openbsd
+ """
+ self.boot_openbsd73("max")
diff --git a/tests/avocado/machine_aarch64_virt.py b/tests/avocado/machine_aarch64_virt.py
new file mode 100644
index 0000000000..a90dc6ff4b
--- /dev/null
+++ b/tests/avocado/machine_aarch64_virt.py
@@ -0,0 +1,146 @@
+# Functional test that boots a various Linux systems and checks the
+# console output.
+#
+# Copyright (c) 2022 Linaro Ltd.
+#
+# Author:
+# Alex Bennée <alex.bennee@linaro.org>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import time
+import os
+import logging
+
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+from avocado_qemu import exec_command
+from avocado_qemu import BUILD_DIR
+from avocado.utils import process
+from avocado.utils.path import find_command
+
+class Aarch64VirtMachine(QemuSystemTest):
+ KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
+ timeout = 360
+
+ def wait_for_console_pattern(self, success_message, vm=None):
+ wait_for_console_pattern(self, success_message,
+ failure_message='Kernel panic - not syncing',
+ vm=vm)
+
+ # This tests the whole boot chain from EFI to Userspace
+ # We only boot a whole OS for the current top level CPU and GIC
+ # Other test profiles should use more minimal boots
+ def test_alpine_virt_tcg_gic_max(self):
+ """
+ :avocado: tags=arch:aarch64
+ :avocado: tags=machine:virt
+ :avocado: tags=accel:tcg
+ """
+ iso_url = ('https://dl-cdn.alpinelinux.org/'
+ 'alpine/v3.17/releases/aarch64/'
+ 'alpine-standard-3.17.2-aarch64.iso')
+
+ # Alpine use sha256 so I recalculated this myself
+ iso_sha1 = '76284fcd7b41fe899b0c2375ceb8470803eea839'
+ iso_path = self.fetch_asset(iso_url, asset_hash=iso_sha1)
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyAMA0')
+ self.require_accelerator("tcg")
+
+ self.vm.add_args("-accel", "tcg")
+ self.vm.add_args("-cpu", "max,pauth-impdef=on")
+ self.vm.add_args("-machine",
+ "virt,acpi=on,"
+ "virtualization=on,"
+ "mte=on,"
+ "gic-version=max,iommu=smmuv3")
+ self.vm.add_args("-smp", "2", "-m", "1024")
+ self.vm.add_args('-bios', os.path.join(BUILD_DIR, 'pc-bios',
+ 'edk2-aarch64-code.fd'))
+ self.vm.add_args("-drive", f"file={iso_path},format=raw")
+ self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0')
+ self.vm.add_args('-object', 'rng-random,id=rng0,filename=/dev/urandom')
+
+ self.vm.launch()
+ self.wait_for_console_pattern('Welcome to Alpine Linux 3.17')
+
+
+ def common_aarch64_virt(self, machine):
+ """
+ Common code to launch basic virt machine with kernel+initrd
+ and a scratch disk.
+ """
+ logger = logging.getLogger('aarch64_virt')
+
+ kernel_url = ('https://fileserver.linaro.org/s/'
+ 'z6B2ARM7DQT3HWN/download')
+ kernel_hash = 'ed11daab50c151dde0e1e9c9cb8b2d9bd3215347'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyAMA0')
+ self.require_accelerator("tcg")
+ self.vm.add_args('-cpu', 'max,pauth-impdef=on',
+ '-machine', machine,
+ '-accel', 'tcg',
+ '-kernel', kernel_path,
+ '-append', kernel_command_line)
+
+ # A RNG offers an easy way to generate a few IRQs
+ self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0')
+ self.vm.add_args('-object',
+ 'rng-random,id=rng0,filename=/dev/urandom')
+
+ # Also add a scratch block device
+ logger.info('creating scratch qcow2 image')
+ image_path = os.path.join(self.workdir, 'scratch.qcow2')
+ qemu_img = os.path.join(BUILD_DIR, 'qemu-img')
+ if not os.path.exists(qemu_img):
+ qemu_img = find_command('qemu-img', False)
+ if qemu_img is False:
+ self.cancel('Could not find "qemu-img", which is required to '
+ 'create the temporary qcow2 image')
+ cmd = '%s create -f qcow2 %s 8M' % (qemu_img, image_path)
+ process.run(cmd)
+
+ # Add the device
+ self.vm.add_args('-blockdev',
+ f"driver=qcow2,file.driver=file,file.filename={image_path},node-name=scratch")
+ self.vm.add_args('-device',
+ 'virtio-blk-device,drive=scratch')
+
+ self.vm.launch()
+ self.wait_for_console_pattern('Welcome to Buildroot')
+ time.sleep(0.1)
+ exec_command(self, 'root')
+ time.sleep(0.1)
+ exec_command(self, 'dd if=/dev/hwrng of=/dev/vda bs=512 count=4')
+ time.sleep(0.1)
+ exec_command(self, 'md5sum /dev/vda')
+ time.sleep(0.1)
+ exec_command(self, 'cat /proc/interrupts')
+ time.sleep(0.1)
+ exec_command(self, 'cat /proc/self/maps')
+ time.sleep(0.1)
+
+ def test_aarch64_virt_gicv3(self):
+ """
+ :avocado: tags=arch:aarch64
+ :avocado: tags=machine:virt
+ :avocado: tags=accel:tcg
+ :avocado: tags=cpu:max
+ """
+ self.common_aarch64_virt("virt,gic_version=3")
+
+ def test_aarch64_virt_gicv2(self):
+ """
+ :avocado: tags=arch:aarch64
+ :avocado: tags=machine:virt
+ :avocado: tags=accel:tcg
+ :avocado: tags=cpu:max
+ """
+ self.common_aarch64_virt("virt,gic-version=2")
diff --git a/tests/avocado/machine_arm_canona1100.py b/tests/avocado/machine_arm_canona1100.py
new file mode 100644
index 0000000000..a42d8b0f2b
--- /dev/null
+++ b/tests/avocado/machine_arm_canona1100.py
@@ -0,0 +1,35 @@
+# Functional test that boots the canon-a1100 machine with firmware
+#
+# Copyright (c) 2020 Red Hat, Inc.
+#
+# Author:
+# Thomas Huth <thuth@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+from avocado.utils import archive
+
+class CanonA1100Machine(QemuSystemTest):
+ """Boots the barebox firmware and checks that the console is operational"""
+
+ timeout = 90
+
+ def test_arm_canona1100(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:canon-a1100
+ :avocado: tags=device:pflash_cfi02
+ """
+ tar_url = ('https://qemu-advcal.gitlab.io'
+ '/qac-best-of-multiarch/download/day18.tar.xz')
+ tar_hash = '068b5fc4242b29381acee94713509f8a876e9db6'
+ file_path = self.fetch_asset(tar_url, asset_hash=tar_hash)
+ archive.extract(file_path, self.workdir)
+ self.vm.set_console()
+ self.vm.add_args('-bios',
+ self.workdir + '/day18/barebox.canon-a1100.bin')
+ self.vm.launch()
+ wait_for_console_pattern(self, 'running /env/bin/init')
diff --git a/tests/avocado/machine_arm_integratorcp.py b/tests/avocado/machine_arm_integratorcp.py
new file mode 100644
index 0000000000..87f5cf3953
--- /dev/null
+++ b/tests/avocado/machine_arm_integratorcp.py
@@ -0,0 +1,99 @@
+# Functional test that boots a Linux kernel and checks the console
+#
+# Copyright (c) 2020 Red Hat, Inc.
+#
+# Author:
+# Thomas Huth <thuth@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import os
+import logging
+
+from avocado import skipUnless
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+
+
+NUMPY_AVAILABLE = True
+try:
+ import numpy as np
+except ImportError:
+ NUMPY_AVAILABLE = False
+
+CV2_AVAILABLE = True
+try:
+ import cv2
+except ImportError:
+ CV2_AVAILABLE = False
+
+
+class IntegratorMachine(QemuSystemTest):
+
+ timeout = 90
+
+ def boot_integratorcp(self):
+ kernel_url = ('https://github.com/zayac/qemu-arm/raw/master/'
+ 'arm-test/kernel/zImage.integrator')
+ kernel_hash = '0d7adba893c503267c946a3cbdc63b4b54f25468'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ initrd_url = ('https://github.com/zayac/qemu-arm/raw/master/'
+ 'arm-test/kernel/arm_root.img')
+ initrd_hash = 'b51e4154285bf784e017a37586428332d8c7bd8b'
+ initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
+
+ self.vm.set_console()
+ self.vm.add_args('-kernel', kernel_path,
+ '-initrd', initrd_path,
+ '-append', 'printk.time=0 console=ttyAMA0')
+ self.vm.launch()
+
+ @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code')
+ def test_integratorcp_console(self):
+ """
+ Boots the Linux kernel and checks that the console is operational
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:integratorcp
+ :avocado: tags=device:pl011
+ """
+ self.boot_integratorcp()
+ wait_for_console_pattern(self, 'Log in as root')
+
+ @skipUnless(NUMPY_AVAILABLE, 'Python NumPy not installed')
+ @skipUnless(CV2_AVAILABLE, 'Python OpenCV not installed')
+ @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code')
+ def test_framebuffer_tux_logo(self):
+ """
+ Boot Linux and verify the Tux logo is displayed on the framebuffer.
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:integratorcp
+ :avocado: tags=device:pl110
+ :avocado: tags=device:framebuffer
+ """
+ screendump_path = os.path.join(self.workdir, "screendump.pbm")
+ tuxlogo_url = ('https://github.com/torvalds/linux/raw/v2.6.12/'
+ 'drivers/video/logo/logo_linux_vga16.ppm')
+ tuxlogo_hash = '3991c2ddbd1ddaecda7601f8aafbcf5b02dc86af'
+ tuxlogo_path = self.fetch_asset(tuxlogo_url, asset_hash=tuxlogo_hash)
+
+ self.boot_integratorcp()
+ framebuffer_ready = 'Console: switching to colour frame buffer device'
+ wait_for_console_pattern(self, framebuffer_ready)
+ self.vm.cmd('human-monitor-command', command_line='stop')
+ self.vm.cmd('human-monitor-command',
+ command_line='screendump %s' % screendump_path)
+ logger = logging.getLogger('framebuffer')
+
+ cpu_count = 1
+ match_threshold = 0.92
+ screendump_bgr = cv2.imread(screendump_path)
+ screendump_gray = cv2.cvtColor(screendump_bgr, cv2.COLOR_BGR2GRAY)
+ result = cv2.matchTemplate(screendump_gray, cv2.imread(tuxlogo_path, 0),
+ cv2.TM_CCOEFF_NORMED)
+ loc = np.where(result >= match_threshold)
+ tux_count = 0
+ for tux_count, pt in enumerate(zip(*loc[::-1]), start=1):
+ logger.debug('found Tux at position [x, y] = %s', pt)
+ self.assertGreaterEqual(tux_count, cpu_count)
diff --git a/tests/avocado/machine_arm_n8x0.py b/tests/avocado/machine_arm_n8x0.py
new file mode 100644
index 0000000000..12e9a6803b
--- /dev/null
+++ b/tests/avocado/machine_arm_n8x0.py
@@ -0,0 +1,49 @@
+# Functional test that boots a Linux kernel and checks the console
+#
+# Copyright (c) 2020 Red Hat, Inc.
+#
+# Author:
+# Thomas Huth <thuth@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import os
+
+from avocado import skipUnless
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+
+class N8x0Machine(QemuSystemTest):
+ """Boots the Linux kernel and checks that the console is operational"""
+
+ timeout = 90
+
+ def __do_test_n8x0(self):
+ kernel_url = ('http://stskeeps.subnetmask.net/meego-n8x0/'
+ 'meego-arm-n8x0-1.0.80.20100712.1431-'
+ 'vmlinuz-2.6.35~rc4-129.1-n8x0')
+ kernel_hash = 'e9d5ab8d7548923a0061b6fbf601465e479ed269'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ self.vm.set_console(console_index=1)
+ self.vm.add_args('-kernel', kernel_path,
+ '-append', 'printk.time=0 console=ttyS1')
+ self.vm.launch()
+ wait_for_console_pattern(self, 'TSC2005 driver initializing')
+
+ @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code')
+ def test_n800(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:n800
+ """
+ self.__do_test_n8x0()
+
+ @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code')
+ def test_n810(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:n810
+ """
+ self.__do_test_n8x0()
diff --git a/tests/avocado/machine_aspeed.py b/tests/avocado/machine_aspeed.py
new file mode 100644
index 0000000000..cec0181424
--- /dev/null
+++ b/tests/avocado/machine_aspeed.py
@@ -0,0 +1,377 @@
+# Functional test that boots the ASPEED SoCs with firmware
+#
+# Copyright (C) 2022 ASPEED Technology Inc
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import time
+import os
+import tempfile
+import subprocess
+
+from avocado_qemu import LinuxSSHMixIn
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+from avocado_qemu import exec_command
+from avocado_qemu import exec_command_and_wait_for_pattern
+from avocado_qemu import interrupt_interactive_console_until_pattern
+from avocado_qemu import has_cmd
+from avocado.utils import archive
+from avocado import skipUnless
+from avocado import skipUnless
+
+
+class AST1030Machine(QemuSystemTest):
+ """Boots the zephyr os and checks that the console is operational"""
+
+ timeout = 10
+
+ def test_ast1030_zephyros_1_04(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:ast1030-evb
+ :avocado: tags=os:zephyr
+ """
+ tar_url = ('https://github.com/AspeedTech-BMC'
+ '/zephyr/releases/download/v00.01.04/ast1030-evb-demo.zip')
+ tar_hash = '4c6a8ce3a8ba76ef1a65dae419ae3409343c4b20'
+ tar_path = self.fetch_asset(tar_url, asset_hash=tar_hash)
+ archive.extract(tar_path, self.workdir)
+ kernel_file = self.workdir + "/ast1030-evb-demo/zephyr.elf"
+ self.vm.set_console()
+ self.vm.add_args('-kernel', kernel_file,
+ '-nographic')
+ self.vm.launch()
+ wait_for_console_pattern(self, "Booting Zephyr OS")
+ exec_command_and_wait_for_pattern(self, "help",
+ "Available commands")
+
+ def test_ast1030_zephyros_1_07(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:ast1030-evb
+ :avocado: tags=os:zephyr
+ """
+ tar_url = ('https://github.com/AspeedTech-BMC'
+ '/zephyr/releases/download/v00.01.07/ast1030-evb-demo.zip')
+ tar_hash = '40ac87eabdcd3b3454ce5aad11fedc72a33ecda2'
+ tar_path = self.fetch_asset(tar_url, asset_hash=tar_hash)
+ archive.extract(tar_path, self.workdir)
+ kernel_file = self.workdir + "/ast1030-evb-demo/zephyr.bin"
+ self.vm.set_console()
+ self.vm.add_args('-kernel', kernel_file,
+ '-nographic')
+ self.vm.launch()
+ wait_for_console_pattern(self, "Booting Zephyr OS")
+ for shell_cmd in [
+ 'kernel stacks',
+ 'otp info conf',
+ 'otp info scu',
+ 'hwinfo devid',
+ 'crypto aes256_cbc_vault',
+ 'random get',
+ 'jtag JTAG1 sw_xfer high TMS',
+ 'adc ADC0 resolution 12',
+ 'adc ADC0 read 42',
+ 'adc ADC1 read 69',
+ 'i2c scan I2C_0',
+ 'i3c attach I3C_0',
+ 'hash test',
+ 'kernel uptime',
+ 'kernel reboot warm',
+ 'kernel uptime',
+ 'kernel reboot cold',
+ 'kernel uptime',
+ ]: exec_command_and_wait_for_pattern(self, shell_cmd, "uart:~$")
+
+class AST2x00Machine(QemuSystemTest):
+
+ timeout = 90
+
+ def wait_for_console_pattern(self, success_message, vm=None):
+ wait_for_console_pattern(self, success_message,
+ failure_message='Kernel panic - not syncing',
+ vm=vm)
+
+ def do_test_arm_aspeed(self, image):
+ self.vm.set_console()
+ self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw',
+ '-net', 'nic')
+ self.vm.launch()
+
+ self.wait_for_console_pattern("U-Boot 2016.07")
+ self.wait_for_console_pattern("## Loading kernel from FIT Image at 20080000")
+ self.wait_for_console_pattern("Starting kernel ...")
+ self.wait_for_console_pattern("Booting Linux on physical CPU 0x0")
+ wait_for_console_pattern(self,
+ "aspeed-smc 1e620000.spi: read control register: 203b0641")
+ self.wait_for_console_pattern("ftgmac100 1e660000.ethernet eth0: irq ")
+ self.wait_for_console_pattern("systemd[1]: Set hostname to")
+
+ def test_arm_ast2400_palmetto_openbmc_v2_9_0(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:palmetto-bmc
+ """
+
+ image_url = ('https://github.com/openbmc/openbmc/releases/download/2.9.0/'
+ 'obmc-phosphor-image-palmetto.static.mtd')
+ image_hash = ('3e13bbbc28e424865dc42f35ad672b10f2e82cdb11846bb28fa625b48beafd0d')
+ image_path = self.fetch_asset(image_url, asset_hash=image_hash,
+ algorithm='sha256')
+
+ self.do_test_arm_aspeed(image_path)
+
+ def test_arm_ast2500_romulus_openbmc_v2_9_0(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:romulus-bmc
+ """
+
+ image_url = ('https://github.com/openbmc/openbmc/releases/download/2.9.0/'
+ 'obmc-phosphor-image-romulus.static.mtd')
+ image_hash = ('820341076803f1955bc31e647a512c79f9add4f5233d0697678bab4604c7bb25')
+ image_path = self.fetch_asset(image_url, asset_hash=image_hash,
+ algorithm='sha256')
+
+ self.do_test_arm_aspeed(image_path)
+
+ def do_test_arm_aspeed_buildroot_start(self, image, cpu_id, pattern='Aspeed EVB'):
+ self.require_netdev('user')
+
+ self.vm.set_console()
+ self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw',
+ '-net', 'nic', '-net', 'user')
+ self.vm.launch()
+
+ self.wait_for_console_pattern('U-Boot 2019.04')
+ self.wait_for_console_pattern('## Loading kernel from FIT Image')
+ self.wait_for_console_pattern('Starting kernel ...')
+ self.wait_for_console_pattern('Booting Linux on physical CPU ' + cpu_id)
+ self.wait_for_console_pattern('lease of 10.0.2.15')
+ # the line before login:
+ self.wait_for_console_pattern(pattern)
+ time.sleep(0.1)
+ exec_command(self, 'root')
+ time.sleep(0.1)
+ exec_command(self, "passw0rd")
+
+ def do_test_arm_aspeed_buildroot_poweroff(self):
+ exec_command_and_wait_for_pattern(self, 'poweroff',
+ 'reboot: System halted');
+
+ def test_arm_ast2500_evb_buildroot(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:ast2500-evb
+ """
+
+ image_url = ('https://github.com/legoater/qemu-aspeed-boot/raw/master/'
+ 'images/ast2500-evb/buildroot-2023.11/flash.img')
+ image_hash = ('c23db6160cf77d0258397eb2051162c8473a56c441417c52a91ba217186e715f')
+ image_path = self.fetch_asset(image_url, asset_hash=image_hash,
+ algorithm='sha256')
+
+ self.vm.add_args('-device',
+ 'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test');
+ self.do_test_arm_aspeed_buildroot_start(image_path, '0x0', 'Aspeed AST2500 EVB')
+
+ exec_command_and_wait_for_pattern(self,
+ 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-3/device/new_device',
+ 'i2c i2c-3: new_device: Instantiated device lm75 at 0x4d');
+ exec_command_and_wait_for_pattern(self,
+ 'cat /sys/class/hwmon/hwmon1/temp1_input', '0')
+ self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test',
+ property='temperature', value=18000);
+ exec_command_and_wait_for_pattern(self,
+ 'cat /sys/class/hwmon/hwmon1/temp1_input', '18000')
+
+ self.do_test_arm_aspeed_buildroot_poweroff()
+
+ def test_arm_ast2600_evb_buildroot(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:ast2600-evb
+ """
+
+ image_url = ('https://github.com/legoater/qemu-aspeed-boot/raw/master/'
+ 'images/ast2600-evb/buildroot-2023.11/flash.img')
+ image_hash = ('b62808daef48b438d0728ee07662290490ecfa65987bb91294cafb1bb7ad1a68')
+ image_path = self.fetch_asset(image_url, asset_hash=image_hash,
+ algorithm='sha256')
+
+ self.vm.add_args('-device',
+ 'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test');
+ self.vm.add_args('-device',
+ 'ds1338,bus=aspeed.i2c.bus.3,address=0x32');
+ self.vm.add_args('-device',
+ 'i2c-echo,bus=aspeed.i2c.bus.3,address=0x42');
+ self.do_test_arm_aspeed_buildroot_start(image_path, '0xf00', 'Aspeed AST2600 EVB')
+
+ exec_command_and_wait_for_pattern(self,
+ 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-3/device/new_device',
+ 'i2c i2c-3: new_device: Instantiated device lm75 at 0x4d');
+ exec_command_and_wait_for_pattern(self,
+ 'cat /sys/class/hwmon/hwmon1/temp1_input', '0')
+ self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test',
+ property='temperature', value=18000);
+ exec_command_and_wait_for_pattern(self,
+ 'cat /sys/class/hwmon/hwmon1/temp1_input', '18000')
+
+ exec_command_and_wait_for_pattern(self,
+ 'echo ds1307 0x32 > /sys/class/i2c-dev/i2c-3/device/new_device',
+ 'i2c i2c-3: new_device: Instantiated device ds1307 at 0x32');
+ year = time.strftime("%Y")
+ exec_command_and_wait_for_pattern(self, 'hwclock -f /dev/rtc1', year);
+
+ exec_command_and_wait_for_pattern(self,
+ 'echo slave-24c02 0x1064 > /sys/bus/i2c/devices/i2c-3/new_device',
+ 'i2c i2c-3: new_device: Instantiated device slave-24c02 at 0x64');
+ exec_command(self, 'i2cset -y 3 0x42 0x64 0x00 0xaa i');
+ time.sleep(0.1)
+ exec_command_and_wait_for_pattern(self,
+ 'hexdump /sys/bus/i2c/devices/3-1064/slave-eeprom',
+ '0000000 ffaa ffff ffff ffff ffff ffff ffff ffff');
+ self.do_test_arm_aspeed_buildroot_poweroff()
+
+ @skipUnless(*has_cmd('swtpm'))
+ def test_arm_ast2600_evb_buildroot_tpm(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:ast2600-evb
+ """
+
+ image_url = ('https://github.com/legoater/qemu-aspeed-boot/raw/master/'
+ 'images/ast2600-evb/buildroot-2023.02-tpm/flash.img')
+ image_hash = ('a46009ae8a5403a0826d607215e731a8c68d27c14c41e55331706b8f9c7bd997')
+ image_path = self.fetch_asset(image_url, asset_hash=image_hash,
+ algorithm='sha256')
+
+ # force creation of VM object, which also defines self._sd
+ vm = self.vm
+
+ socket = os.path.join(self._sd.name, 'swtpm-socket')
+
+ subprocess.run(['swtpm', 'socket', '-d', '--tpm2',
+ '--tpmstate', f'dir={self.vm.temp_dir}',
+ '--ctrl', f'type=unixio,path={socket}'])
+
+ self.vm.add_args('-chardev', f'socket,id=chrtpm,path={socket}')
+ self.vm.add_args('-tpmdev', 'emulator,id=tpm0,chardev=chrtpm')
+ self.vm.add_args('-device',
+ 'tpm-tis-i2c,tpmdev=tpm0,bus=aspeed.i2c.bus.12,address=0x2e')
+ self.do_test_arm_aspeed_buildroot_start(image_path, '0xf00', 'Aspeed AST2600 EVB')
+
+ exec_command_and_wait_for_pattern(self,
+ 'echo tpm_tis_i2c 0x2e > /sys/bus/i2c/devices/i2c-12/new_device',
+ 'tpm_tis_i2c 12-002e: 2.0 TPM (device-id 0x1, rev-id 1)');
+ exec_command_and_wait_for_pattern(self,
+ 'cat /sys/class/tpm/tpm0/pcr-sha256/0',
+ 'B804724EA13F52A9072BA87FE8FDCC497DFC9DF9AA15B9088694639C431688E0');
+
+ self.do_test_arm_aspeed_buildroot_poweroff()
+
+class AST2x00MachineSDK(QemuSystemTest, LinuxSSHMixIn):
+
+ EXTRA_BOOTARGS = (
+ 'quiet '
+ 'systemd.mask=org.openbmc.HostIpmi.service '
+ 'systemd.mask=xyz.openbmc_project.Chassis.Control.Power@0.service '
+ 'systemd.mask=modprobe@fuse.service '
+ 'systemd.mask=rngd.service '
+ 'systemd.mask=obmc-console@ttyS2.service '
+ )
+
+ # FIXME: Although these tests boot a whole distro they are still
+ # slower than comparable machine models. There may be some
+ # optimisations which bring down the runtime. In the meantime they
+ # have generous timeouts and are disable for CI which aims for all
+ # tests to run in less than 60 seconds.
+ timeout = 240
+
+ def wait_for_console_pattern(self, success_message, vm=None):
+ wait_for_console_pattern(self, success_message,
+ failure_message='Kernel panic - not syncing',
+ vm=vm)
+
+ def do_test_arm_aspeed_sdk_start(self, image):
+ self.require_netdev('user')
+ self.vm.set_console()
+ self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw',
+ '-net', 'nic', '-net', 'user,hostfwd=:127.0.0.1:0-:22')
+ self.vm.launch()
+
+ self.wait_for_console_pattern('U-Boot 2019.04')
+ interrupt_interactive_console_until_pattern(
+ self, 'Hit any key to stop autoboot:', 'ast#')
+ exec_command_and_wait_for_pattern(
+ self, 'setenv bootargs ${bootargs} ' + self.EXTRA_BOOTARGS, 'ast#')
+ exec_command_and_wait_for_pattern(
+ self, 'boot', '## Loading kernel from FIT Image')
+ self.wait_for_console_pattern('Starting kernel ...')
+
+ @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab')
+
+ def test_arm_ast2500_evb_sdk(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:ast2500-evb
+ :avocado: tags=flaky
+ """
+
+ image_url = ('https://github.com/AspeedTech-BMC/openbmc/releases/'
+ 'download/v08.06/ast2500-default-obmc.tar.gz')
+ image_hash = ('e1755f3cadff69190438c688d52dd0f0d399b70a1e14b1d3d5540fc4851d38ca')
+ image_path = self.fetch_asset(image_url, asset_hash=image_hash,
+ algorithm='sha256')
+ archive.extract(image_path, self.workdir)
+
+ self.do_test_arm_aspeed_sdk_start(
+ self.workdir + '/ast2500-default/image-bmc')
+ self.wait_for_console_pattern('nodistro.0 ast2500-default ttyS4')
+
+ @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab')
+
+ def test_arm_ast2600_evb_sdk(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:ast2600-evb
+ :avocado: tags=flaky
+ """
+
+ image_url = ('https://github.com/AspeedTech-BMC/openbmc/releases/'
+ 'download/v08.06/ast2600-a2-obmc.tar.gz')
+ image_hash = ('9083506135f622d5e7351fcf7d4e1c7125cee5ba16141220c0ba88931f3681a4')
+ image_path = self.fetch_asset(image_url, asset_hash=image_hash,
+ algorithm='sha256')
+ archive.extract(image_path, self.workdir)
+
+ self.vm.add_args('-device',
+ 'tmp105,bus=aspeed.i2c.bus.5,address=0x4d,id=tmp-test');
+ self.vm.add_args('-device',
+ 'ds1338,bus=aspeed.i2c.bus.5,address=0x32');
+ self.do_test_arm_aspeed_sdk_start(
+ self.workdir + '/ast2600-a2/image-bmc')
+ self.wait_for_console_pattern('nodistro.0 ast2600-a2 ttyS4')
+
+ self.ssh_connect('root', '0penBmc', False)
+ self.ssh_command('dmesg -c > /dev/null')
+
+ self.ssh_command_output_contains(
+ 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-5/device/new_device ; '
+ 'dmesg -c',
+ 'i2c i2c-5: new_device: Instantiated device lm75 at 0x4d');
+ self.ssh_command_output_contains(
+ 'cat /sys/class/hwmon/hwmon19/temp1_input', '0')
+ self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test',
+ property='temperature', value=18000);
+ self.ssh_command_output_contains(
+ 'cat /sys/class/hwmon/hwmon19/temp1_input', '18000')
+
+ self.ssh_command_output_contains(
+ 'echo ds1307 0x32 > /sys/class/i2c-dev/i2c-5/device/new_device ; '
+ 'dmesg -c',
+ 'i2c i2c-5: new_device: Instantiated device ds1307 at 0x32');
+ year = time.strftime("%Y")
+ self.ssh_command_output_contains('/sbin/hwclock -f /dev/rtc1', year);
diff --git a/tests/avocado/machine_avr6.py b/tests/avocado/machine_avr6.py
new file mode 100644
index 0000000000..5485db79c6
--- /dev/null
+++ b/tests/avocado/machine_avr6.py
@@ -0,0 +1,50 @@
+#
+# QEMU AVR integration tests
+#
+# Copyright (c) 2019-2020 Michael Rolnik <mrolnik@gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import time
+
+from avocado_qemu import QemuSystemTest
+
+class AVR6Machine(QemuSystemTest):
+ timeout = 5
+
+ def test_freertos(self):
+ """
+ :avocado: tags=arch:avr
+ :avocado: tags=machine:arduino-mega-2560-v3
+ """
+ """
+ https://github.com/seharris/qemu-avr-tests/raw/master/free-rtos/Demo/AVR_ATMega2560_GCC/demo.elf
+ constantly prints out 'ABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWX'
+ """
+ rom_url = ('https://github.com/seharris/qemu-avr-tests'
+ '/raw/36c3e67b8755dcf/free-rtos/Demo'
+ '/AVR_ATMega2560_GCC/demo.elf')
+ rom_hash = '7eb521f511ca8f2622e0a3c5e8dd686efbb911d4'
+ rom_path = self.fetch_asset(rom_url, asset_hash=rom_hash)
+
+ self.vm.add_args('-bios', rom_path)
+ self.vm.add_args('-nographic')
+ self.vm.launch()
+
+ time.sleep(2)
+ self.vm.shutdown()
+
+ self.assertIn('ABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWX',
+ self.vm.get_log())
diff --git a/tests/avocado/machine_loongarch.py b/tests/avocado/machine_loongarch.py
new file mode 100644
index 0000000000..7d8a3c1fa5
--- /dev/null
+++ b/tests/avocado/machine_loongarch.py
@@ -0,0 +1,58 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# LoongArch virt test.
+#
+# Copyright (c) 2023 Loongson Technology Corporation Limited
+#
+
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import exec_command_and_wait_for_pattern
+from avocado_qemu import wait_for_console_pattern
+
+class LoongArchMachine(QemuSystemTest):
+ KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
+
+ timeout = 120
+
+ def wait_for_console_pattern(self, success_message, vm=None):
+ wait_for_console_pattern(self, success_message,
+ failure_message='Kernel panic - not syncing',
+ vm=vm)
+
+ def test_loongarch64_devices(self):
+
+ """
+ :avocado: tags=arch:loongarch64
+ :avocado: tags=machine:virt
+ """
+
+ kernel_url = ('https://github.com/yangxiaojuan-loongson/qemu-binary/'
+ 'releases/download/binary-files/vmlinuz.efi')
+ kernel_hash = '951b485b16e3788b6db03a3e1793c067009e31a2'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ initrd_url = ('https://github.com/yangxiaojuan-loongson/qemu-binary/'
+ 'releases/download/binary-files/ramdisk')
+ initrd_hash = 'c67658d9b2a447ce7db2f73ba3d373c9b2b90ab2'
+ initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
+
+ bios_url = ('https://github.com/yangxiaojuan-loongson/qemu-binary/'
+ 'releases/download/binary-files/QEMU_EFI.fd')
+ bios_hash = ('dfc1bfba4853cd763b9d392d0031827e8addbca8')
+ bios_path = self.fetch_asset(bios_url, asset_hash=bios_hash)
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'root=/dev/ram rdinit=/sbin/init console=ttyS0,115200')
+ self.vm.add_args('-nographic',
+ '-smp', '4',
+ '-m', '1024',
+ '-cpu', 'la464',
+ '-kernel', kernel_path,
+ '-initrd', initrd_path,
+ '-bios', bios_path,
+ '-append', kernel_command_line)
+ self.vm.launch()
+ self.wait_for_console_pattern('Run /sbin/init as init process')
+ exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo',
+ 'processor : 3')
diff --git a/tests/avocado/machine_m68k_nextcube.py b/tests/avocado/machine_m68k_nextcube.py
new file mode 100644
index 0000000000..1f3c883910
--- /dev/null
+++ b/tests/avocado/machine_m68k_nextcube.py
@@ -0,0 +1,70 @@
+# Functional test that boots a VM and run OCR on the framebuffer
+#
+# Copyright (c) 2019 Philippe Mathieu-Daudé <f4bug@amsat.org>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import os
+import time
+
+from avocado_qemu import QemuSystemTest
+from avocado import skipUnless
+
+from tesseract_utils import tesseract_available, tesseract_ocr
+
+PIL_AVAILABLE = True
+try:
+ from PIL import Image
+except ImportError:
+ PIL_AVAILABLE = False
+
+
+class NextCubeMachine(QemuSystemTest):
+ """
+ :avocado: tags=arch:m68k
+ :avocado: tags=machine:next-cube
+ :avocado: tags=device:framebuffer
+ """
+
+ timeout = 15
+
+ def check_bootrom_framebuffer(self, screenshot_path):
+ rom_url = ('https://sourceforge.net/p/previous/code/1350/tree/'
+ 'trunk/src/Rev_2.5_v66.BIN?format=raw')
+ rom_hash = 'b3534796abae238a0111299fc406a9349f7fee24'
+ rom_path = self.fetch_asset(rom_url, asset_hash=rom_hash)
+
+ self.vm.add_args('-bios', rom_path)
+ self.vm.launch()
+
+ self.log.info('VM launched, waiting for display')
+ # TODO: Use avocado.utils.wait.wait_for to catch the
+ # 'displaysurface_create 1120x832' trace-event.
+ time.sleep(2)
+
+ self.vm.cmd('human-monitor-command',
+ command_line='screendump %s' % screenshot_path)
+
+ @skipUnless(PIL_AVAILABLE, 'Python PIL not installed')
+ def test_bootrom_framebuffer_size(self):
+ screenshot_path = os.path.join(self.workdir, "dump.ppm")
+ self.check_bootrom_framebuffer(screenshot_path)
+
+ width, height = Image.open(screenshot_path).size
+ self.assertEqual(width, 1120)
+ self.assertEqual(height, 832)
+
+ # Tesseract 4 adds a new OCR engine based on LSTM neural networks. The
+ # new version is faster and more accurate than version 3. The drawback is
+ # that it is still alpha-level software.
+ @skipUnless(tesseract_available(4), 'tesseract OCR tool not available')
+ def test_bootrom_framebuffer_ocr_with_tesseract(self):
+ screenshot_path = os.path.join(self.workdir, "dump.ppm")
+ self.check_bootrom_framebuffer(screenshot_path)
+ lines = tesseract_ocr(screenshot_path, tesseract_version=4)
+ text = '\n'.join(lines)
+ self.assertIn('Testing the FPU', text)
+ self.assertIn('System test failed. Error code', text)
+ self.assertIn('Boot command', text)
+ self.assertIn('Next>', text)
diff --git a/tests/avocado/machine_microblaze.py b/tests/avocado/machine_microblaze.py
new file mode 100644
index 0000000000..807709cd11
--- /dev/null
+++ b/tests/avocado/machine_microblaze.py
@@ -0,0 +1,61 @@
+# Functional test that boots a microblaze Linux kernel and checks the console
+#
+# Copyright (c) 2018, 2021 Red Hat, Inc.
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import time
+from avocado_qemu import exec_command, exec_command_and_wait_for_pattern
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+from avocado.utils import archive
+
+class MicroblazeMachine(QemuSystemTest):
+
+ timeout = 90
+
+ def test_microblaze_s3adsp1800(self):
+ """
+ :avocado: tags=arch:microblaze
+ :avocado: tags=machine:petalogix-s3adsp1800
+ """
+
+ tar_url = ('https://qemu-advcal.gitlab.io'
+ '/qac-best-of-multiarch/download/day17.tar.xz')
+ tar_hash = '08bf3e3bfb6b6c7ce1e54ab65d54e189f2caf13f'
+ file_path = self.fetch_asset(tar_url, asset_hash=tar_hash)
+ archive.extract(file_path, self.workdir)
+ self.vm.set_console()
+ self.vm.add_args('-kernel', self.workdir + '/day17/ballerina.bin')
+ self.vm.launch()
+ wait_for_console_pattern(self, 'This architecture does not have '
+ 'kernel memory protection')
+ # Note:
+ # The kernel sometimes gets stuck after the "This architecture ..."
+ # message, that's why we don't test for a later string here. This
+ # needs some investigation by a microblaze wizard one day...
+
+ def test_microblazeel_s3adsp1800(self):
+ """
+ :avocado: tags=arch:microblazeel
+ :avocado: tags=machine:petalogix-s3adsp1800
+ """
+
+ self.require_netdev('user')
+ tar_url = ('http://www.qemu-advent-calendar.org/2023/download/'
+ 'day13.tar.gz')
+ tar_hash = '6623d5fff5f84cfa8f34e286f32eff6a26546f44'
+ file_path = self.fetch_asset(tar_url, asset_hash=tar_hash)
+ archive.extract(file_path, self.workdir)
+ self.vm.set_console()
+ self.vm.add_args('-kernel', self.workdir + '/day13/xmaton.bin')
+ self.vm.add_args('-nic', 'user,tftp=' + self.workdir + '/day13/')
+ self.vm.launch()
+ wait_for_console_pattern(self, 'QEMU Advent Calendar 2023')
+ time.sleep(0.1)
+ exec_command(self, 'root')
+ time.sleep(0.1)
+ exec_command_and_wait_for_pattern(self,
+ 'tftp -g -r xmaton.png 10.0.2.2 ; md5sum xmaton.png',
+ '821cd3cab8efd16ad6ee5acc3642a8ea')
diff --git a/tests/avocado/machine_mips_fuloong2e.py b/tests/avocado/machine_mips_fuloong2e.py
new file mode 100644
index 0000000000..89291f47b2
--- /dev/null
+++ b/tests/avocado/machine_mips_fuloong2e.py
@@ -0,0 +1,42 @@
+# Functional tests for the Lemote Fuloong-2E machine.
+#
+# Copyright (c) 2019 Philippe Mathieu-Daudé <f4bug@amsat.org>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import os
+
+from avocado import skipUnless
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+
+class MipsFuloong2e(QemuSystemTest):
+
+ timeout = 60
+
+ @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code')
+ @skipUnless(os.getenv('RESCUE_YL_PATH'), 'RESCUE_YL_PATH not available')
+ def test_linux_kernel_isa_serial(self):
+ """
+ :avocado: tags=arch:mips64el
+ :avocado: tags=machine:fuloong2e
+ :avocado: tags=endian:little
+ :avocado: tags=device:bonito64
+ :avocado: tags=device:via686b
+ """
+ # Recovery system for the Yeeloong laptop
+ # (enough to test the fuloong2e southbridge, accessing its ISA bus)
+ # http://dev.lemote.com/files/resource/download/rescue/rescue-yl
+ kernel_hash = 'ec4d1bd89a8439c41033ca63db60160cc6d6f09a'
+ kernel_path = self.fetch_asset('file://' + os.getenv('RESCUE_YL_PATH'),
+ asset_hash=kernel_hash)
+
+ self.vm.set_console()
+ self.vm.add_args('-kernel', kernel_path)
+ self.vm.launch()
+ wait_for_console_pattern(self, 'Linux version 2.6.27.7lemote')
+ cpu_revision = 'CPU revision is: 00006302 (ICT Loongson-2)'
+ wait_for_console_pattern(self, cpu_revision)
diff --git a/tests/avocado/machine_mips_loongson3v.py b/tests/avocado/machine_mips_loongson3v.py
new file mode 100644
index 0000000000..5194cf18c9
--- /dev/null
+++ b/tests/avocado/machine_mips_loongson3v.py
@@ -0,0 +1,39 @@
+# Functional tests for the Generic Loongson-3 Platform.
+#
+# Copyright (c) 2021 Jiaxun Yang <jiaxun.yang@flygoat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import os
+import time
+
+from avocado import skipUnless
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+
+class MipsLoongson3v(QemuSystemTest):
+ timeout = 60
+
+ @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code')
+ def test_pmon_serial_console(self):
+ """
+ :avocado: tags=arch:mips64el
+ :avocado: tags=endian:little
+ :avocado: tags=machine:loongson3-virt
+ :avocado: tags=cpu:Loongson-3A1000
+ :avocado: tags=device:liointc
+ :avocado: tags=device:goldfish_rtc
+ """
+
+ pmon_hash = '7c8b45dd81ccfc55ff28f5aa267a41c3'
+ pmon_path = self.fetch_asset('https://github.com/loongson-community/pmon/'
+ 'releases/download/20210112/pmon-3avirt.bin',
+ asset_hash=pmon_hash, algorithm='md5')
+
+ self.vm.set_console()
+ self.vm.add_args('-bios', pmon_path)
+ self.vm.launch()
+ wait_for_console_pattern(self, 'CPU GODSON3 BogoMIPS:')
diff --git a/tests/avocado/machine_mips_malta.py b/tests/avocado/machine_mips_malta.py
new file mode 100644
index 0000000000..8cf84bd805
--- /dev/null
+++ b/tests/avocado/machine_mips_malta.py
@@ -0,0 +1,164 @@
+# Functional tests for the MIPS Malta board
+#
+# Copyright (c) Philippe Mathieu-Daudé <f4bug@amsat.org>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import os
+import gzip
+import logging
+
+from avocado import skipUnless
+from avocado import skipUnless
+from avocado.utils import archive
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import exec_command_and_wait_for_pattern
+from avocado_qemu import interrupt_interactive_console_until_pattern
+from avocado_qemu import wait_for_console_pattern
+
+
+NUMPY_AVAILABLE = True
+try:
+ import numpy as np
+except ImportError:
+ NUMPY_AVAILABLE = False
+
+CV2_AVAILABLE = True
+try:
+ import cv2
+except ImportError:
+ CV2_AVAILABLE = False
+
+
+@skipUnless(NUMPY_AVAILABLE, 'Python NumPy not installed')
+@skipUnless(CV2_AVAILABLE, 'Python OpenCV not installed')
+class MaltaMachineFramebuffer(QemuSystemTest):
+
+ timeout = 30
+
+ KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
+
+ def do_test_i6400_framebuffer_logo(self, cpu_cores_count):
+ """
+ Boot Linux kernel and check Tux logo is displayed on the framebuffer.
+ """
+ screendump_path = os.path.join(self.workdir, 'screendump.pbm')
+
+ kernel_url = ('https://github.com/philmd/qemu-testing-blob/raw/'
+ 'a5966ca4b5/mips/malta/mips64el/'
+ 'vmlinux-4.7.0-rc1.I6400.gz')
+ kernel_hash = '096f50c377ec5072e6a366943324622c312045f6'
+ kernel_path_gz = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+ kernel_path = self.workdir + "vmlinux"
+ archive.gzip_uncompress(kernel_path_gz, kernel_path)
+
+ tuxlogo_url = ('https://github.com/torvalds/linux/raw/v2.6.12/'
+ 'drivers/video/logo/logo_linux_vga16.ppm')
+ tuxlogo_hash = '3991c2ddbd1ddaecda7601f8aafbcf5b02dc86af'
+ tuxlogo_path = self.fetch_asset(tuxlogo_url, asset_hash=tuxlogo_hash)
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'clocksource=GIC console=tty0 console=ttyS0')
+ self.vm.add_args('-kernel', kernel_path,
+ '-smp', '%u' % cpu_cores_count,
+ '-vga', 'std',
+ '-append', kernel_command_line)
+ self.vm.launch()
+ framebuffer_ready = 'Console: switching to colour frame buffer device'
+ wait_for_console_pattern(self, framebuffer_ready,
+ failure_message='Kernel panic - not syncing')
+ self.vm.cmd('human-monitor-command', command_line='stop')
+ self.vm.cmd('human-monitor-command',
+ command_line='screendump %s' % screendump_path)
+ logger = logging.getLogger('framebuffer')
+
+ match_threshold = 0.95
+ screendump_bgr = cv2.imread(screendump_path, cv2.IMREAD_COLOR)
+ tuxlogo_bgr = cv2.imread(tuxlogo_path, cv2.IMREAD_COLOR)
+ result = cv2.matchTemplate(screendump_bgr, tuxlogo_bgr,
+ cv2.TM_CCOEFF_NORMED)
+ loc = np.where(result >= match_threshold)
+ tuxlogo_count = 0
+ h, w = tuxlogo_bgr.shape[:2]
+ debug_png = os.getenv('AVOCADO_CV2_SCREENDUMP_PNG_PATH')
+ for tuxlogo_count, pt in enumerate(zip(*loc[::-1]), start=1):
+ logger.debug('found Tux at position (x, y) = %s', pt)
+ cv2.rectangle(screendump_bgr, pt,
+ (pt[0] + w, pt[1] + h), (0, 0, 255), 2)
+ if debug_png:
+ cv2.imwrite(debug_png, screendump_bgr)
+ self.assertGreaterEqual(tuxlogo_count, cpu_cores_count)
+
+ def test_mips_malta_i6400_framebuffer_logo_1core(self):
+ """
+ :avocado: tags=arch:mips64el
+ :avocado: tags=machine:malta
+ :avocado: tags=cpu:I6400
+ """
+ self.do_test_i6400_framebuffer_logo(1)
+
+ @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab')
+
+ def test_mips_malta_i6400_framebuffer_logo_7cores(self):
+ """
+ :avocado: tags=arch:mips64el
+ :avocado: tags=machine:malta
+ :avocado: tags=cpu:I6400
+ :avocado: tags=mips:smp
+ :avocado: tags=flaky
+ """
+ self.do_test_i6400_framebuffer_logo(7)
+
+ @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab')
+
+ def test_mips_malta_i6400_framebuffer_logo_8cores(self):
+ """
+ :avocado: tags=arch:mips64el
+ :avocado: tags=machine:malta
+ :avocado: tags=cpu:I6400
+ :avocado: tags=mips:smp
+ :avocado: tags=flaky
+ """
+ self.do_test_i6400_framebuffer_logo(8)
+
+class MaltaMachine(QemuSystemTest):
+
+ def do_test_yamon(self):
+ rom_url = ('https://s3-eu-west-1.amazonaws.com/'
+ 'downloads-mips/mips-downloads/'
+ 'YAMON/yamon-bin-02.22.zip')
+ rom_hash = '8da7ecddbc5312704b8b324341ee238189bde480'
+ zip_path = self.fetch_asset(rom_url, asset_hash=rom_hash)
+
+ archive.extract(zip_path, self.workdir)
+ yamon_path = os.path.join(self.workdir, 'yamon-02.22.bin')
+
+ self.vm.set_console()
+ self.vm.add_args('-bios', yamon_path)
+ self.vm.launch()
+
+ prompt = 'YAMON>'
+ pattern = 'YAMON ROM Monitor'
+ interrupt_interactive_console_until_pattern(self, pattern, prompt)
+ wait_for_console_pattern(self, prompt)
+ self.vm.shutdown()
+
+ def test_mipsel_malta_yamon(self):
+ """
+ :avocado: tags=arch:mipsel
+ :avocado: tags=machine:malta
+ :avocado: tags=endian:little
+ """
+ self.do_test_yamon()
+
+ def test_mips64el_malta_yamon(self):
+ """
+ :avocado: tags=arch:mips64el
+ :avocado: tags=machine:malta
+ :avocado: tags=endian:little
+ """
+ self.do_test_yamon()
diff --git a/tests/avocado/machine_rx_gdbsim.py b/tests/avocado/machine_rx_gdbsim.py
new file mode 100644
index 0000000000..412a7a5089
--- /dev/null
+++ b/tests/avocado/machine_rx_gdbsim.py
@@ -0,0 +1,77 @@
+# Functional test that boots a Linux kernel and checks the console
+#
+# Copyright (c) 2018 Red Hat, Inc.
+#
+# Author:
+# Cleber Rosa <crosa@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import os
+
+from avocado import skipUnless
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import exec_command_and_wait_for_pattern
+from avocado_qemu import wait_for_console_pattern
+from avocado.utils import archive
+
+
+class RxGdbSimMachine(QemuSystemTest):
+
+ timeout = 30
+ KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
+
+ @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab')
+
+ def test_uboot(self):
+ """
+ U-Boot and checks that the console is operational.
+
+ :avocado: tags=arch:rx
+ :avocado: tags=machine:gdbsim-r5f562n8
+ :avocado: tags=endian:little
+ :avocado: tags=flaky
+ """
+ uboot_url = ('https://acc.dl.osdn.jp/users/23/23888/u-boot.bin.gz')
+ uboot_hash = '9b78dbd43b40b2526848c0b1ce9de02c24f4dcdb'
+ uboot_path = self.fetch_asset(uboot_url, asset_hash=uboot_hash)
+ uboot_path = archive.uncompress(uboot_path, self.workdir)
+
+ self.vm.set_console()
+ self.vm.add_args('-bios', uboot_path,
+ '-no-reboot')
+ self.vm.launch()
+ uboot_version = 'U-Boot 2016.05-rc3-23705-ga1ef3c71cb-dirty'
+ wait_for_console_pattern(self, uboot_version)
+ gcc_version = 'rx-unknown-linux-gcc (GCC) 9.0.0 20181105 (experimental)'
+ # FIXME limit baudrate on chardev, else we type too fast
+ #exec_command_and_wait_for_pattern(self, 'version', gcc_version)
+
+ @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab')
+
+ def test_linux_sash(self):
+ """
+ Boots a Linux kernel and checks that the console is operational.
+
+ :avocado: tags=arch:rx
+ :avocado: tags=machine:gdbsim-r5f562n7
+ :avocado: tags=endian:little
+ :avocado: tags=flaky
+ """
+ dtb_url = ('https://acc.dl.osdn.jp/users/23/23887/rx-virt.dtb')
+ dtb_hash = '7b4e4e2c71905da44e86ce47adee2210b026ac18'
+ dtb_path = self.fetch_asset(dtb_url, asset_hash=dtb_hash)
+ kernel_url = ('http://acc.dl.osdn.jp/users/23/23845/zImage')
+ kernel_hash = '39a81067f8d72faad90866ddfefa19165d68fc99'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ self.vm.set_console()
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'earlycon'
+ self.vm.add_args('-kernel', kernel_path,
+ '-dtb', dtb_path,
+ '-no-reboot')
+ self.vm.launch()
+ wait_for_console_pattern(self, 'Sash command shell (version 1.1.1)',
+ failure_message='Kernel panic - not syncing')
+ exec_command_and_wait_for_pattern(self, 'printenv', 'TERM=linux')
diff --git a/tests/avocado/machine_s390_ccw_virtio.py b/tests/avocado/machine_s390_ccw_virtio.py
new file mode 100644
index 0000000000..26e938c9e9
--- /dev/null
+++ b/tests/avocado/machine_s390_ccw_virtio.py
@@ -0,0 +1,277 @@
+# Functional test that boots an s390x Linux guest with ccw and PCI devices
+# attached and checks whether the devices are recognized by Linux
+#
+# Copyright (c) 2020 Red Hat, Inc.
+#
+# Author:
+# Cornelia Huck <cohuck@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import os
+import tempfile
+
+from avocado import skipUnless
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import exec_command_and_wait_for_pattern
+from avocado_qemu import wait_for_console_pattern
+from avocado.utils import archive
+
+class S390CCWVirtioMachine(QemuSystemTest):
+ KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
+
+ timeout = 120
+
+ def wait_for_console_pattern(self, success_message, vm=None):
+ wait_for_console_pattern(self, success_message,
+ failure_message='Kernel panic - not syncing',
+ vm=vm)
+
+ def wait_for_crw_reports(self):
+ exec_command_and_wait_for_pattern(self,
+ 'while ! (dmesg -c | grep CRW) ; do sleep 1 ; done',
+ 'CRW reports')
+
+ dmesg_clear_count = 1
+ def clear_guest_dmesg(self):
+ exec_command_and_wait_for_pattern(self, 'dmesg -c > /dev/null; '
+ r'echo dm_clear\ ' + str(self.dmesg_clear_count),
+ r'dm_clear ' + str(self.dmesg_clear_count))
+ self.dmesg_clear_count += 1
+
+ def test_s390x_devices(self):
+
+ """
+ :avocado: tags=arch:s390x
+ :avocado: tags=machine:s390-ccw-virtio
+ """
+
+ kernel_url = ('https://snapshot.debian.org/archive/debian/'
+ '20201126T092837Z/dists/buster/main/installer-s390x/'
+ '20190702+deb10u6/images/generic/kernel.debian')
+ kernel_hash = '5821fbee57d6220a067a8b967d24595621aa1eb6'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ initrd_url = ('https://snapshot.debian.org/archive/debian/'
+ '20201126T092837Z/dists/buster/main/installer-s390x/'
+ '20190702+deb10u6/images/generic/initrd.debian')
+ initrd_hash = '81ba09c97bef46e8f4660ac25b4ac0a5be3a94d6'
+ initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=sclp0 root=/dev/ram0 BOOT_DEBUG=3')
+ self.vm.add_args('-nographic',
+ '-kernel', kernel_path,
+ '-initrd', initrd_path,
+ '-append', kernel_command_line,
+ '-cpu', 'max,prno-trng=off',
+ '-device', 'virtio-net-ccw,devno=fe.1.1111',
+ '-device',
+ 'virtio-rng-ccw,devno=fe.2.0000,max_revision=0,id=rn1',
+ '-device',
+ 'virtio-rng-ccw,devno=fe.3.1234,max_revision=2,id=rn2',
+ '-device', 'zpci,uid=5,target=zzz',
+ '-device', 'virtio-net-pci,id=zzz',
+ '-device', 'zpci,uid=0xa,fid=12,target=serial',
+ '-device', 'virtio-serial-pci,id=serial',
+ '-device', 'virtio-balloon-ccw')
+ self.vm.launch()
+
+ shell_ready = "sh: can't access tty; job control turned off"
+ self.wait_for_console_pattern(shell_ready)
+ # first debug shell is too early, we need to wait for device detection
+ exec_command_and_wait_for_pattern(self, 'exit', shell_ready)
+
+ ccw_bus_ids="0.1.1111 0.2.0000 0.3.1234"
+ pci_bus_ids="0005:00:00.0 000a:00:00.0"
+ exec_command_and_wait_for_pattern(self, 'ls /sys/bus/ccw/devices/',
+ ccw_bus_ids)
+ exec_command_and_wait_for_pattern(self, 'ls /sys/bus/pci/devices/',
+ pci_bus_ids)
+ # check that the device at 0.2.0000 is in legacy mode, while the
+ # device at 0.3.1234 has the virtio-1 feature bit set
+ virtio_rng_features="00000000000000000000000000001100" + \
+ "10000000000000000000000000000000"
+ virtio_rng_features_legacy="00000000000000000000000000001100" + \
+ "00000000000000000000000000000000"
+ exec_command_and_wait_for_pattern(self,
+ 'cat /sys/bus/ccw/devices/0.2.0000/virtio?/features',
+ virtio_rng_features_legacy)
+ exec_command_and_wait_for_pattern(self,
+ 'cat /sys/bus/ccw/devices/0.3.1234/virtio?/features',
+ virtio_rng_features)
+ # check that /dev/hwrng works - and that it's gone after ejecting
+ exec_command_and_wait_for_pattern(self,
+ 'dd if=/dev/hwrng of=/dev/null bs=1k count=10',
+ '10+0 records out')
+ self.clear_guest_dmesg()
+ self.vm.cmd('device_del', id='rn1')
+ self.wait_for_crw_reports()
+ self.clear_guest_dmesg()
+ self.vm.cmd('device_del', id='rn2')
+ self.wait_for_crw_reports()
+ exec_command_and_wait_for_pattern(self,
+ 'dd if=/dev/hwrng of=/dev/null bs=1k count=10',
+ 'dd: /dev/hwrng: No such device')
+ # verify that we indeed have virtio-net devices (without having the
+ # virtio-net driver handy)
+ exec_command_and_wait_for_pattern(self,
+ 'cat /sys/bus/ccw/devices/0.1.1111/cutype',
+ '3832/01')
+ exec_command_and_wait_for_pattern(self,
+ r'cat /sys/bus/pci/devices/0005\:00\:00.0/subsystem_vendor',
+ r'0x1af4')
+ exec_command_and_wait_for_pattern(self,
+ r'cat /sys/bus/pci/devices/0005\:00\:00.0/subsystem_device',
+ r'0x0001')
+ # check fid propagation
+ exec_command_and_wait_for_pattern(self,
+ r'cat /sys/bus/pci/devices/000a\:00\:00.0/function_id',
+ r'0x0000000c')
+ # add another device
+ self.clear_guest_dmesg()
+ self.vm.cmd('device_add', driver='virtio-net-ccw',
+ devno='fe.0.4711', id='net_4711')
+ self.wait_for_crw_reports()
+ exec_command_and_wait_for_pattern(self, 'for i in 1 2 3 4 5 6 7 ; do '
+ 'if [ -e /sys/bus/ccw/devices/*4711 ]; then break; fi ;'
+ 'sleep 1 ; done ; ls /sys/bus/ccw/devices/',
+ '0.0.4711')
+ # and detach it again
+ self.clear_guest_dmesg()
+ self.vm.cmd('device_del', id='net_4711')
+ self.vm.event_wait(name='DEVICE_DELETED',
+ match={'data': {'device': 'net_4711'}})
+ self.wait_for_crw_reports()
+ exec_command_and_wait_for_pattern(self,
+ 'ls /sys/bus/ccw/devices/0.0.4711',
+ 'No such file or directory')
+ # test the virtio-balloon device
+ exec_command_and_wait_for_pattern(self, 'head -n 1 /proc/meminfo',
+ 'MemTotal: 115640 kB')
+ self.vm.cmd('human-monitor-command', command_line='balloon 96')
+ exec_command_and_wait_for_pattern(self, 'head -n 1 /proc/meminfo',
+ 'MemTotal: 82872 kB')
+ self.vm.cmd('human-monitor-command', command_line='balloon 128')
+ exec_command_and_wait_for_pattern(self, 'head -n 1 /proc/meminfo',
+ 'MemTotal: 115640 kB')
+
+
+ def test_s390x_fedora(self):
+
+ """
+ :avocado: tags=arch:s390x
+ :avocado: tags=machine:s390-ccw-virtio
+ :avocado: tags=device:virtio-gpu
+ :avocado: tags=device:virtio-crypto
+ :avocado: tags=device:virtio-net
+ :avocado: tags=flaky
+ """
+
+ kernel_url = ('https://archives.fedoraproject.org/pub/archive'
+ '/fedora-secondary/releases/31/Server/s390x/os'
+ '/images/kernel.img')
+ kernel_hash = 'b93d1efcafcf29c1673a4ce371a1f8b43941cfeb'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ initrd_url = ('https://archives.fedoraproject.org/pub/archive'
+ '/fedora-secondary/releases/31/Server/s390x/os'
+ '/images/initrd.img')
+ initrd_hash = '3de45d411df5624b8d8ef21cd0b44419ab59b12f'
+ initrd_path_xz = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
+ initrd_path = os.path.join(self.workdir, 'initrd-raw.img')
+ archive.lzma_uncompress(initrd_path_xz, initrd_path)
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + ' audit=0 '
+ 'rd.plymouth=0 plymouth.enable=0 rd.rescue')
+ self.vm.add_args('-nographic',
+ '-smp', '4',
+ '-m', '512',
+ '-name', 'Some Guest Name',
+ '-uuid', '30de4fd9-b4d5-409e-86a5-09b387f70bfa',
+ '-kernel', kernel_path,
+ '-initrd', initrd_path,
+ '-append', kernel_command_line,
+ '-device', 'zpci,uid=7,target=n',
+ '-device', 'virtio-net-pci,id=n,mac=02:ca:fe:fa:ce:12',
+ '-device', 'virtio-rng-ccw,devno=fe.1.9876',
+ '-device', 'virtio-gpu-ccw,devno=fe.2.5432')
+ self.vm.launch()
+ self.wait_for_console_pattern('Entering emergency mode')
+
+ # Some tests to see whether the CLI options have been considered:
+ self.log.info("Test whether QEMU CLI options have been considered")
+ exec_command_and_wait_for_pattern(self,
+ 'while ! (dmesg | grep enP7p0s0) ; do sleep 1 ; done',
+ 'virtio_net virtio0 enP7p0s0: renamed')
+ exec_command_and_wait_for_pattern(self, 'lspci',
+ '0007:00:00.0 Class 0200: Device 1af4:1000')
+ exec_command_and_wait_for_pattern(self,
+ 'cat /sys/class/net/enP7p0s0/address',
+ '02:ca:fe:fa:ce:12')
+ exec_command_and_wait_for_pattern(self, 'lscss', '0.1.9876')
+ exec_command_and_wait_for_pattern(self, 'lscss', '0.2.5432')
+ exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo',
+ 'processors : 4')
+ exec_command_and_wait_for_pattern(self, 'grep MemTotal /proc/meminfo',
+ 'MemTotal: 499848 kB')
+ exec_command_and_wait_for_pattern(self, 'grep Name /proc/sysinfo',
+ 'Extended Name: Some Guest Name')
+ exec_command_and_wait_for_pattern(self, 'grep UUID /proc/sysinfo',
+ '30de4fd9-b4d5-409e-86a5-09b387f70bfa')
+
+ # Disable blinking cursor, then write some stuff into the framebuffer.
+ # QEMU's PPM screendumps contain uncompressed 24-bit values, while the
+ # framebuffer uses 32-bit, so we pad our text with some spaces when
+ # writing to the framebuffer. Since the PPM is uncompressed, we then
+ # can simply read the written "magic bytes" back from the PPM file to
+ # check whether the framebuffer is working as expected.
+ # Unfortunately, this test is flaky, so we don't run it by default
+ if os.getenv('QEMU_TEST_FLAKY_TESTS'):
+ self.log.info("Test screendump of virtio-gpu device")
+ exec_command_and_wait_for_pattern(self,
+ 'while ! (dmesg | grep gpudrmfb) ; do sleep 1 ; done',
+ 'virtio_gpudrmfb frame buffer device')
+ exec_command_and_wait_for_pattern(self,
+ r'echo -e "\e[?25l" > /dev/tty0', ':/#')
+ exec_command_and_wait_for_pattern(self, 'for ((i=0;i<250;i++)); do '
+ 'echo " The qu ick fo x j ump s o ver a laz y d og" >> fox.txt;'
+ 'done',
+ ':/#')
+ exec_command_and_wait_for_pattern(self,
+ 'dd if=fox.txt of=/dev/fb0 bs=1000 oflag=sync,nocache ; rm fox.txt',
+ '12+0 records out')
+ with tempfile.NamedTemporaryFile(suffix='.ppm',
+ prefix='qemu-scrdump-') as ppmfile:
+ self.vm.cmd('screendump', filename=ppmfile.name)
+ ppmfile.seek(0)
+ line = ppmfile.readline()
+ self.assertEqual(line, b"P6\n")
+ line = ppmfile.readline()
+ self.assertEqual(line, b"1280 800\n")
+ line = ppmfile.readline()
+ self.assertEqual(line, b"255\n")
+ line = ppmfile.readline(256)
+ self.assertEqual(line, b"The quick fox jumps over a lazy dog\n")
+ else:
+ self.log.info("Skipped flaky screendump of virtio-gpu device test")
+
+ # Hot-plug a virtio-crypto device and see whether it gets accepted
+ self.log.info("Test hot-plug virtio-crypto device")
+ self.clear_guest_dmesg()
+ self.vm.cmd('object-add', qom_type='cryptodev-backend-builtin',
+ id='cbe0')
+ self.vm.cmd('device_add', driver='virtio-crypto-ccw', id='crypdev0',
+ cryptodev='cbe0', devno='fe.0.2342')
+ exec_command_and_wait_for_pattern(self,
+ 'while ! (dmesg -c | grep Accelerator.device) ; do'
+ ' sleep 1 ; done', 'Accelerator device is ready')
+ exec_command_and_wait_for_pattern(self, 'lscss', '0.0.2342')
+ self.vm.cmd('device_del', id='crypdev0')
+ self.vm.cmd('object-del', id='cbe0')
+ exec_command_and_wait_for_pattern(self,
+ 'while ! (dmesg -c | grep Start.virtcrypto_remove) ; do'
+ ' sleep 1 ; done', 'Start virtcrypto_remove.')
diff --git a/tests/avocado/machine_sparc64_sun4u.py b/tests/avocado/machine_sparc64_sun4u.py
new file mode 100644
index 0000000000..d333c0ae91
--- /dev/null
+++ b/tests/avocado/machine_sparc64_sun4u.py
@@ -0,0 +1,36 @@
+# Functional test that boots a Linux kernel and checks the console
+#
+# Copyright (c) 2020 Red Hat, Inc.
+#
+# Author:
+# Thomas Huth <thuth@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import os
+
+from avocado_qemu import wait_for_console_pattern
+from avocado.utils import archive
+from boot_linux_console import LinuxKernelTest
+
+class Sun4uMachine(LinuxKernelTest):
+ """Boots the Linux kernel and checks that the console is operational"""
+
+ timeout = 90
+
+ def test_sparc64_sun4u(self):
+ """
+ :avocado: tags=arch:sparc64
+ :avocado: tags=machine:sun4u
+ """
+ tar_url = ('https://qemu-advcal.gitlab.io'
+ '/qac-best-of-multiarch/download/day23.tar.xz')
+ tar_hash = '142db83cd974ffadc4f75c8a5cad5bcc5722c240'
+ file_path = self.fetch_asset(tar_url, asset_hash=tar_hash)
+ archive.extract(file_path, self.workdir)
+ self.vm.set_console()
+ self.vm.add_args('-kernel', self.workdir + '/day23/vmlinux',
+ '-append', self.KERNEL_COMMON_COMMAND_LINE)
+ self.vm.launch()
+ wait_for_console_pattern(self, 'Starting logging: OK')
diff --git a/tests/avocado/machine_sparc_leon3.py b/tests/avocado/machine_sparc_leon3.py
new file mode 100644
index 0000000000..e61b223185
--- /dev/null
+++ b/tests/avocado/machine_sparc_leon3.py
@@ -0,0 +1,37 @@
+# Functional test that boots a Leon3 machine and checks its serial console.
+#
+# Copyright (c) Philippe Mathieu-Daudé <f4bug@amsat.org>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+from avocado import skip
+
+
+class Leon3Machine(QemuSystemTest):
+
+ timeout = 60
+
+ @skip("Test currently broken")
+ # A Window Underflow exception occurs before booting the kernel,
+ # and QEMU exit calling cpu_abort(), which makes this test to fail.
+ def test_leon3_helenos_uimage(self):
+ """
+ :avocado: tags=arch:sparc
+ :avocado: tags=machine:leon3_generic
+ :avocado: tags=binfmt:uimage
+ """
+ kernel_url = ('http://www.helenos.org/releases/'
+ 'HelenOS-0.6.0-sparc32-leon3.bin')
+ kernel_hash = 'a88c9cfdb8430c66650e5290a08765f9bf049a30'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ self.vm.set_console()
+ self.vm.add_args('-kernel', kernel_path)
+
+ self.vm.launch()
+
+ wait_for_console_pattern(self, 'Copyright (c) 2001-2014 HelenOS project')
+ wait_for_console_pattern(self, 'Booting the kernel ...')
diff --git a/tests/avocado/mem-addr-space-check.py b/tests/avocado/mem-addr-space-check.py
new file mode 100644
index 0000000000..af019969c0
--- /dev/null
+++ b/tests/avocado/mem-addr-space-check.py
@@ -0,0 +1,356 @@
+# Check for crash when using memory beyond the available guest processor
+# address space.
+#
+# Copyright (c) 2023 Red Hat, Inc.
+#
+# Author:
+# Ani Sinha <anisinha@redhat.com>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+from avocado_qemu import QemuSystemTest
+import signal
+import time
+
+class MemAddrCheck(QemuSystemTest):
+ # after launch, in order to generate the logs from QEMU we need to
+ # wait for some time. Launching and then immediately shutting down
+ # the VM generates empty logs. A delay of 1 second is added for
+ # this reason.
+ DELAY_Q35_BOOT_SEQUENCE = 1
+
+ # first, lets test some 32-bit processors.
+ # for all 32-bit cases, pci64_hole_size is 0.
+ def test_phybits_low_pse36(self):
+ """
+ :avocado: tags=machine:q35
+ :avocado: tags=arch:x86_64
+
+ With pse36 feature ON, a processor has 36 bits of addressing. So it can
+ access up to a maximum of 64GiB of memory. Memory hotplug region begins
+ at 4 GiB boundary when "above_4g_mem_size" is 0 (this would be true when
+ we have 0.5 GiB of VM memory, see pc_q35_init()). This means total
+ hotpluggable memory size is 60 GiB. Per slot, we reserve 1 GiB of memory
+ for dimm alignment for all newer machines (see enforce_aligned_dimm
+ property for pc machines and pc_get_device_memory_range()). That leaves
+ total hotpluggable actual memory size of 59 GiB. If the VM is started
+ with 0.5 GiB of memory, maxmem should be set to a maximum value of
+ 59.5 GiB to ensure that the processor can address all memory directly.
+ Note that 64-bit pci hole size is 0 in this case. If maxmem is set to
+ 59.6G, QEMU should fail to start with a message "phy-bits are too low".
+ If maxmem is set to 59.5G with all other QEMU parameters identical, QEMU
+ should start fine.
+ """
+ self.vm.add_args('-S', '-machine', 'q35', '-m',
+ '512,slots=1,maxmem=59.6G',
+ '-cpu', 'pentium,pse36=on', '-display', 'none',
+ '-object', 'memory-backend-ram,id=mem1,size=1G',
+ '-device', 'pc-dimm,id=vm0,memdev=mem1')
+ self.vm.set_qmp_monitor(enabled=False)
+ self.vm.launch()
+ self.vm.wait()
+ self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
+ self.assertRegex(self.vm.get_log(), r'phys-bits too low')
+
+ def test_phybits_low_pae(self):
+ """
+ :avocado: tags=machine:q35
+ :avocado: tags=arch:x86_64
+
+ With pae feature ON, a processor has 36 bits of addressing. So it can
+ access up to a maximum of 64GiB of memory. Rest is the same as the case
+ with pse36 above.
+ """
+ self.vm.add_args('-S', '-machine', 'q35', '-m',
+ '512,slots=1,maxmem=59.6G',
+ '-cpu', 'pentium,pae=on', '-display', 'none',
+ '-object', 'memory-backend-ram,id=mem1,size=1G',
+ '-device', 'pc-dimm,id=vm0,memdev=mem1')
+ self.vm.set_qmp_monitor(enabled=False)
+ self.vm.launch()
+ self.vm.wait()
+ self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
+ self.assertRegex(self.vm.get_log(), r'phys-bits too low')
+
+ def test_phybits_ok_pentium_pse36(self):
+ """
+ :avocado: tags=machine:q35
+ :avocado: tags=arch:x86_64
+
+ Setting maxmem to 59.5G and making sure that QEMU can start with the
+ same options as the failing case above with pse36 cpu feature.
+ """
+ self.vm.add_args('-machine', 'q35', '-m',
+ '512,slots=1,maxmem=59.5G',
+ '-cpu', 'pentium,pse36=on', '-display', 'none',
+ '-object', 'memory-backend-ram,id=mem1,size=1G',
+ '-device', 'pc-dimm,id=vm0,memdev=mem1')
+ self.vm.set_qmp_monitor(enabled=False)
+ self.vm.launch()
+ time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
+ self.vm.shutdown()
+ self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')
+
+ def test_phybits_ok_pentium_pae(self):
+ """
+ :avocado: tags=machine:q35
+ :avocado: tags=arch:x86_64
+
+ Test is same as above but now with pae cpu feature turned on.
+ Setting maxmem to 59.5G and making sure that QEMU can start fine
+ with the same options as the case above.
+ """
+ self.vm.add_args('-machine', 'q35', '-m',
+ '512,slots=1,maxmem=59.5G',
+ '-cpu', 'pentium,pae=on', '-display', 'none',
+ '-object', 'memory-backend-ram,id=mem1,size=1G',
+ '-device', 'pc-dimm,id=vm0,memdev=mem1')
+ self.vm.set_qmp_monitor(enabled=False)
+ self.vm.launch()
+ time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
+ self.vm.shutdown()
+ self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')
+
+ def test_phybits_ok_pentium2(self):
+ """
+ :avocado: tags=machine:q35
+ :avocado: tags=arch:x86_64
+
+ Pentium2 has 36 bits of addressing, so its same as pentium
+ with pse36 ON.
+ """
+ self.vm.add_args('-machine', 'q35', '-m',
+ '512,slots=1,maxmem=59.5G',
+ '-cpu', 'pentium2', '-display', 'none',
+ '-object', 'memory-backend-ram,id=mem1,size=1G',
+ '-device', 'pc-dimm,id=vm0,memdev=mem1')
+ self.vm.set_qmp_monitor(enabled=False)
+ self.vm.launch()
+ time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
+ self.vm.shutdown()
+ self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')
+
+ def test_phybits_low_nonpse36(self):
+ """
+ :avocado: tags=machine:q35
+ :avocado: tags=arch:x86_64
+
+ Pentium processor has 32 bits of addressing without pse36 or pae
+ so it can access physical address up to 4 GiB. Setting maxmem to
+ 4 GiB should make QEMU fail to start with "phys-bits too low"
+ message because the region for memory hotplug is always placed
+ above 4 GiB due to the PCI hole and simplicity.
+ """
+ self.vm.add_args('-S', '-machine', 'q35', '-m',
+ '512,slots=1,maxmem=4G',
+ '-cpu', 'pentium', '-display', 'none',
+ '-object', 'memory-backend-ram,id=mem1,size=1G',
+ '-device', 'pc-dimm,id=vm0,memdev=mem1')
+ self.vm.set_qmp_monitor(enabled=False)
+ self.vm.launch()
+ self.vm.wait()
+ self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
+ self.assertRegex(self.vm.get_log(), r'phys-bits too low')
+
+ # now lets test some 64-bit CPU cases.
+ def test_phybits_low_tcg_q35_70_amd(self):
+ """
+ :avocado: tags=machine:q35
+ :avocado: tags=arch:x86_64
+
+ For q35 7.1 machines and above, there is a HT window that starts at
+ 1024 GiB and ends at 1 TiB - 1. If the max GPA falls in this range,
+ "above_4G" memory is adjusted to start at 1 TiB boundary for AMD cpus
+ in the default case. Lets test without that case for machines 7.0.
+ For q35-7.0 machines, "above 4G" memory starts are 4G.
+ pci64_hole size is 32 GiB. Since TCG_PHYS_ADDR_BITS is defined to
+ be 40, TCG emulated CPUs have maximum of 1 TiB (1024 GiB) of
+ directly addressable memory.
+ Hence, maxmem value at most can be
+ 1024 GiB - 4 GiB - 1 GiB per slot for alignment - 32 GiB + 0.5 GiB
+ which is equal to 987.5 GiB. Setting the value to 988 GiB should
+ make QEMU fail with the error message.
+ """
+ self.vm.add_args('-S', '-machine', 'pc-q35-7.0', '-m',
+ '512,slots=1,maxmem=988G',
+ '-display', 'none',
+ '-object', 'memory-backend-ram,id=mem1,size=1G',
+ '-device', 'pc-dimm,id=vm0,memdev=mem1')
+ self.vm.set_qmp_monitor(enabled=False)
+ self.vm.launch()
+ self.vm.wait()
+ self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
+ self.assertRegex(self.vm.get_log(), r'phys-bits too low')
+
+ def test_phybits_low_tcg_q35_71_amd(self):
+ """
+ :avocado: tags=machine:q35
+ :avocado: tags=arch:x86_64
+
+ AMD_HT_START is defined to be at 1012 GiB. So for q35 machines
+ version > 7.0 and AMD cpus, instead of 1024 GiB limit for 40 bit
+ processor address space, it has to be 1012 GiB , that is 12 GiB
+ less than the case above in order to accommodate HT hole.
+ Make sure QEMU fails when maxmem size is 976 GiB (12 GiB less
+ than 988 GiB).
+ """
+ self.vm.add_args('-S', '-machine', 'pc-q35-7.1', '-m',
+ '512,slots=1,maxmem=976G',
+ '-display', 'none',
+ '-object', 'memory-backend-ram,id=mem1,size=1G',
+ '-device', 'pc-dimm,id=vm0,memdev=mem1')
+ self.vm.set_qmp_monitor(enabled=False)
+ self.vm.launch()
+ self.vm.wait()
+ self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
+ self.assertRegex(self.vm.get_log(), r'phys-bits too low')
+
+ def test_phybits_ok_tcg_q35_70_amd(self):
+ """
+ :avocado: tags=machine:q35
+ :avocado: tags=arch:x86_64
+
+ Same as q35-7.0 AMD case except that here we check that QEMU can
+ successfully start when maxmem is < 988G.
+ """
+ self.vm.add_args('-S', '-machine', 'pc-q35-7.0', '-m',
+ '512,slots=1,maxmem=987.5G',
+ '-display', 'none',
+ '-object', 'memory-backend-ram,id=mem1,size=1G',
+ '-device', 'pc-dimm,id=vm0,memdev=mem1')
+ self.vm.set_qmp_monitor(enabled=False)
+ self.vm.launch()
+ time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
+ self.vm.shutdown()
+ self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')
+
+ def test_phybits_ok_tcg_q35_71_amd(self):
+ """
+ :avocado: tags=machine:q35
+ :avocado: tags=arch:x86_64
+
+ Same as q35-7.1 AMD case except that here we check that QEMU can
+ successfully start when maxmem is < 976G.
+ """
+ self.vm.add_args('-S', '-machine', 'pc-q35-7.1', '-m',
+ '512,slots=1,maxmem=975.5G',
+ '-display', 'none',
+ '-object', 'memory-backend-ram,id=mem1,size=1G',
+ '-device', 'pc-dimm,id=vm0,memdev=mem1')
+ self.vm.set_qmp_monitor(enabled=False)
+ self.vm.launch()
+ time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
+ self.vm.shutdown()
+ self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')
+
+ def test_phybits_ok_tcg_q35_71_intel(self):
+ """
+ :avocado: tags=machine:q35
+ :avocado: tags=arch:x86_64
+
+ Same parameters as test_phybits_low_tcg_q35_71_amd() but use
+ Intel cpu instead. QEMU should start fine in this case as
+ "above_4G" memory starts at 4G.
+ """
+ self.vm.add_args('-S', '-cpu', 'Skylake-Server',
+ '-machine', 'pc-q35-7.1', '-m',
+ '512,slots=1,maxmem=976G',
+ '-display', 'none',
+ '-object', 'memory-backend-ram,id=mem1,size=1G',
+ '-device', 'pc-dimm,id=vm0,memdev=mem1')
+ self.vm.set_qmp_monitor(enabled=False)
+ self.vm.launch()
+ time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
+ self.vm.shutdown()
+ self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')
+
+ def test_phybits_low_tcg_q35_71_amd_41bits(self):
+ """
+ :avocado: tags=machine:q35
+ :avocado: tags=arch:x86_64
+
+ AMD processor with 41 bits. Max cpu hw address = 2 TiB.
+ By setting maxram above 1012 GiB - 32 GiB - 4 GiB = 976 GiB, we can
+ force "above_4G" memory to start at 1 TiB for q35-7.1 machines
+ (max GPA will be above AMD_HT_START which is defined as 1012 GiB).
+
+ With pci_64_hole size at 32 GiB, in this case, maxmem should be 991.5
+ GiB with 1 GiB per slot for alignment and 0.5 GiB as non-hotplug
+ memory for the VM (1024 - 32 - 1 + 0.5). With 992 GiB, QEMU should
+ fail to start.
+ """
+ self.vm.add_args('-S', '-cpu', 'EPYC-v4,phys-bits=41',
+ '-machine', 'pc-q35-7.1', '-m',
+ '512,slots=1,maxmem=992G',
+ '-display', 'none',
+ '-object', 'memory-backend-ram,id=mem1,size=1G',
+ '-device', 'pc-dimm,id=vm0,memdev=mem1')
+ self.vm.set_qmp_monitor(enabled=False)
+ self.vm.launch()
+ self.vm.wait()
+ self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
+ self.assertRegex(self.vm.get_log(), r'phys-bits too low')
+
+ def test_phybits_ok_tcg_q35_71_amd_41bits(self):
+ """
+ :avocado: tags=machine:q35
+ :avocado: tags=arch:x86_64
+
+ AMD processor with 41 bits. Max cpu hw address = 2 TiB.
+ Same as above but by setting maxram between 976 GiB and 992 Gib,
+ QEMU should start fine.
+ """
+ self.vm.add_args('-S', '-cpu', 'EPYC-v4,phys-bits=41',
+ '-machine', 'pc-q35-7.1', '-m',
+ '512,slots=1,maxmem=990G',
+ '-display', 'none',
+ '-object', 'memory-backend-ram,id=mem1,size=1G',
+ '-device', 'pc-dimm,id=vm0,memdev=mem1')
+ self.vm.set_qmp_monitor(enabled=False)
+ self.vm.launch()
+ time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
+ self.vm.shutdown()
+ self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')
+
+ def test_phybits_low_tcg_q35_intel_cxl(self):
+ """
+ :avocado: tags=machine:q35
+ :avocado: tags=arch:x86_64
+
+ cxl memory window starts after memory device range. Here, we use 1 GiB
+ of cxl window memory. 4G_mem end aligns at 4G. pci64_hole is 32 GiB and
+ starts after the cxl memory window.
+ So maxmem here should be at most 986 GiB considering all memory boundary
+ alignment constraints with 40 bits (1 TiB) of processor physical bits.
+ """
+ self.vm.add_args('-S', '-cpu', 'Skylake-Server,phys-bits=40',
+ '-machine', 'q35,cxl=on', '-m',
+ '512,slots=1,maxmem=987G',
+ '-display', 'none',
+ '-device', 'pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1',
+ '-M', 'cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=1G')
+ self.vm.set_qmp_monitor(enabled=False)
+ self.vm.launch()
+ self.vm.wait()
+ self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1")
+ self.assertRegex(self.vm.get_log(), r'phys-bits too low')
+
+ def test_phybits_ok_tcg_q35_intel_cxl(self):
+ """
+ :avocado: tags=machine:q35
+ :avocado: tags=arch:x86_64
+
+ Same as above but here we do not reserve any cxl memory window. Hence,
+ with the exact same parameters as above, QEMU should start fine even
+ with cxl enabled.
+ """
+ self.vm.add_args('-S', '-cpu', 'Skylake-Server,phys-bits=40',
+ '-machine', 'q35,cxl=on', '-m',
+ '512,slots=1,maxmem=987G',
+ '-display', 'none',
+ '-device', 'pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1')
+ self.vm.set_qmp_monitor(enabled=False)
+ self.vm.launch()
+ time.sleep(self.DELAY_Q35_BOOT_SEQUENCE)
+ self.vm.shutdown()
+ self.assertNotRegex(self.vm.get_log(), r'phys-bits too low')
diff --git a/tests/avocado/migration.py b/tests/avocado/migration.py
new file mode 100644
index 0000000000..be6234b3c2
--- /dev/null
+++ b/tests/avocado/migration.py
@@ -0,0 +1,135 @@
+# Migration test
+#
+# Copyright (c) 2019 Red Hat, Inc.
+#
+# Authors:
+# Cleber Rosa <crosa@redhat.com>
+# Caio Carrara <ccarrara@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+
+import tempfile
+import os
+
+from avocado_qemu import QemuSystemTest
+from avocado import skipUnless
+
+from avocado.utils.network import ports
+from avocado.utils import wait
+from avocado.utils.path import find_command
+
+
+class MigrationTest(QemuSystemTest):
+ """
+ :avocado: tags=migration
+ """
+
+ timeout = 10
+
+ @staticmethod
+ def migration_finished(vm):
+ return vm.cmd('query-migrate')['status'] in ('completed', 'failed')
+
+ def assert_migration(self, src_vm, dst_vm):
+ wait.wait_for(self.migration_finished,
+ timeout=self.timeout,
+ step=0.1,
+ args=(src_vm,))
+ wait.wait_for(self.migration_finished,
+ timeout=self.timeout,
+ step=0.1,
+ args=(dst_vm,))
+ self.assertEqual(src_vm.cmd('query-migrate')['status'], 'completed')
+ self.assertEqual(dst_vm.cmd('query-migrate')['status'], 'completed')
+ self.assertEqual(dst_vm.cmd('query-status')['status'], 'running')
+ self.assertEqual(src_vm.cmd('query-status')['status'],'postmigrate')
+
+ def do_migrate(self, dest_uri, src_uri=None):
+ dest_vm = self.get_vm('-incoming', dest_uri)
+ dest_vm.add_args('-nodefaults')
+ dest_vm.launch()
+ if src_uri is None:
+ src_uri = dest_uri
+ source_vm = self.get_vm()
+ source_vm.add_args('-nodefaults')
+ source_vm.launch()
+ source_vm.qmp('migrate', uri=src_uri)
+ self.assert_migration(source_vm, dest_vm)
+
+ def _get_free_port(self):
+ port = ports.find_free_port()
+ if port is None:
+ self.cancel('Failed to find a free port')
+ return port
+
+ def migration_with_tcp_localhost(self):
+ dest_uri = 'tcp:localhost:%u' % self._get_free_port()
+ self.do_migrate(dest_uri)
+
+ def migration_with_unix(self):
+ with tempfile.TemporaryDirectory(prefix='socket_') as socket_path:
+ dest_uri = 'unix:%s/qemu-test.sock' % socket_path
+ self.do_migrate(dest_uri)
+
+ @skipUnless(find_command('nc', default=False), "'nc' command not found")
+ def migration_with_exec(self):
+ """The test works for both netcat-traditional and netcat-openbsd packages."""
+ free_port = self._get_free_port()
+ dest_uri = 'exec:nc -l localhost %u' % free_port
+ src_uri = 'exec:nc localhost %u' % free_port
+ self.do_migrate(dest_uri, src_uri)
+
+
+@skipUnless('aarch64' in os.uname()[4], "host != target")
+class Aarch64(MigrationTest):
+ """
+ :avocado: tags=arch:aarch64
+ :avocado: tags=machine:virt
+ :avocado: tags=cpu:max
+ """
+
+ def test_migration_with_tcp_localhost(self):
+ self.migration_with_tcp_localhost()
+
+ def test_migration_with_unix(self):
+ self.migration_with_unix()
+
+ def test_migration_with_exec(self):
+ self.migration_with_exec()
+
+
+@skipUnless('x86_64' in os.uname()[4], "host != target")
+class X86_64(MigrationTest):
+ """
+ :avocado: tags=arch:x86_64
+ :avocado: tags=machine:pc
+ :avocado: tags=cpu:qemu64
+ """
+
+ def test_migration_with_tcp_localhost(self):
+ self.migration_with_tcp_localhost()
+
+ def test_migration_with_unix(self):
+ self.migration_with_unix()
+
+ def test_migration_with_exec(self):
+ self.migration_with_exec()
+
+
+@skipUnless('ppc64le' in os.uname()[4], "host != target")
+class PPC64(MigrationTest):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:pseries
+ """
+
+ def test_migration_with_tcp_localhost(self):
+ self.migration_with_tcp_localhost()
+
+ def test_migration_with_unix(self):
+ self.migration_with_unix()
+
+ def test_migration_with_exec(self):
+ self.migration_with_exec()
diff --git a/tests/avocado/multiprocess.py b/tests/avocado/multiprocess.py
new file mode 100644
index 0000000000..ee7490ae08
--- /dev/null
+++ b/tests/avocado/multiprocess.py
@@ -0,0 +1,102 @@
+# Test for multiprocess qemu
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+
+import os
+import socket
+
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+from avocado_qemu import exec_command
+from avocado_qemu import exec_command_and_wait_for_pattern
+
+class Multiprocess(QemuSystemTest):
+ """
+ :avocado: tags=multiprocess
+ """
+ KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
+
+ def do_test(self, kernel_url, kernel_hash, initrd_url, initrd_hash,
+ kernel_command_line, machine_type):
+ """Main test method"""
+ self.require_accelerator('kvm')
+ self.require_multiprocess()
+
+ # Create socketpair to connect proxy and remote processes
+ proxy_sock, remote_sock = socket.socketpair(socket.AF_UNIX,
+ socket.SOCK_STREAM)
+ os.set_inheritable(proxy_sock.fileno(), True)
+ os.set_inheritable(remote_sock.fileno(), True)
+
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+ initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
+
+ # Create remote process
+ remote_vm = self.get_vm()
+ remote_vm.add_args('-machine', 'x-remote')
+ remote_vm.add_args('-nodefaults')
+ remote_vm.add_args('-device', 'lsi53c895a,id=lsi1')
+ remote_vm.add_args('-object', 'x-remote-object,id=robj1,'
+ 'devid=lsi1,fd='+str(remote_sock.fileno()))
+ remote_vm.launch()
+
+ # Create proxy process
+ self.vm.set_console()
+ self.vm.add_args('-machine', machine_type)
+ self.vm.add_args('-accel', 'kvm')
+ self.vm.add_args('-cpu', 'host')
+ self.vm.add_args('-object',
+ 'memory-backend-memfd,id=sysmem-file,size=2G')
+ self.vm.add_args('--numa', 'node,memdev=sysmem-file')
+ self.vm.add_args('-m', '2048')
+ self.vm.add_args('-kernel', kernel_path,
+ '-initrd', initrd_path,
+ '-append', kernel_command_line)
+ self.vm.add_args('-device',
+ 'x-pci-proxy-dev,'
+ 'id=lsi1,fd='+str(proxy_sock.fileno()))
+ self.vm.launch()
+ wait_for_console_pattern(self, 'as init process',
+ 'Kernel panic - not syncing')
+ exec_command(self, 'mount -t sysfs sysfs /sys')
+ exec_command_and_wait_for_pattern(self,
+ 'cat /sys/bus/pci/devices/*/uevent',
+ 'PCI_ID=1000:0012')
+
+ def test_multiprocess_x86_64(self):
+ """
+ :avocado: tags=arch:x86_64
+ """
+ kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora'
+ '/linux/releases/31/Everything/x86_64/os/images'
+ '/pxeboot/vmlinuz')
+ kernel_hash = '5b6f6876e1b5bda314f93893271da0d5777b1f3c'
+ initrd_url = ('https://archives.fedoraproject.org/pub/archive/fedora'
+ '/linux/releases/31/Everything/x86_64/os/images'
+ '/pxeboot/initrd.img')
+ initrd_hash = 'dd0340a1b39bd28f88532babd4581c67649ec5b1'
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyS0 rdinit=/bin/bash')
+ machine_type = 'pc'
+ self.do_test(kernel_url, kernel_hash, initrd_url, initrd_hash,
+ kernel_command_line, machine_type)
+
+ def test_multiprocess_aarch64(self):
+ """
+ :avocado: tags=arch:aarch64
+ """
+ kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora'
+ '/linux/releases/31/Everything/aarch64/os/images'
+ '/pxeboot/vmlinuz')
+ kernel_hash = '3505f2751e2833c681de78cee8dda1e49cabd2e8'
+ initrd_url = ('https://archives.fedoraproject.org/pub/archive/fedora'
+ '/linux/releases/31/Everything/aarch64/os/images'
+ '/pxeboot/initrd.img')
+ initrd_hash = '519a1962daf17d67fc3a9c89d45affcb399607db'
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'rdinit=/bin/bash console=ttyAMA0')
+ machine_type = 'virt,gic-version=3'
+ self.do_test(kernel_url, kernel_hash, initrd_url, initrd_hash,
+ kernel_command_line, machine_type)
diff --git a/tests/avocado/netdev-ethtool.py b/tests/avocado/netdev-ethtool.py
new file mode 100644
index 0000000000..5f33288f81
--- /dev/null
+++ b/tests/avocado/netdev-ethtool.py
@@ -0,0 +1,101 @@
+# ethtool tests for emulated network devices
+#
+# This test leverages ethtool's --test sequence to validate network
+# device behaviour.
+#
+# SPDX-License-Identifier: GPL-2.0-or-late
+
+from avocado import skip
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+
+class NetDevEthtool(QemuSystemTest):
+ """
+ :avocado: tags=arch:x86_64
+ :avocado: tags=machine:q35
+ """
+
+ # Runs in about 17s under KVM, 19s under TCG, 25s under GCOV
+ timeout = 45
+
+ # Fetch assets from the netdev-ethtool subdir of my shared test
+ # images directory on fileserver.linaro.org.
+ def get_asset(self, name, sha1):
+ base_url = ('https://fileserver.linaro.org/s/'
+ 'kE4nCFLdQcoBF9t/download?'
+ 'path=%2Fnetdev-ethtool&files=' )
+ url = base_url + name
+ # use explicit name rather than failing to neatly parse the
+ # URL into a unique one
+ return self.fetch_asset(name=name, locations=(url), asset_hash=sha1)
+
+ def common_test_code(self, netdev, extra_args=None):
+
+ # This custom kernel has drivers for all the supported network
+ # devices we can emulate in QEMU
+ kernel = self.get_asset("bzImage",
+ "33469d7802732d5815226166581442395cb289e2")
+
+ rootfs = self.get_asset("rootfs.squashfs",
+ "9793cea7021414ae844bda51f558bd6565b50cdc")
+
+ append = 'printk.time=0 console=ttyS0 '
+ append += 'root=/dev/sr0 rootfstype=squashfs '
+
+ # any additional kernel tweaks for the test
+ if extra_args:
+ append += extra_args
+
+ # finally invoke ethtool directly
+ append += ' init=/usr/sbin/ethtool -- -t eth1 offline'
+
+ # add the rootfs via a readonly cdrom image
+ drive = f"file={rootfs},if=ide,index=0,media=cdrom"
+
+ self.vm.add_args('-kernel', kernel,
+ '-append', append,
+ '-drive', drive,
+ '-device', netdev)
+
+ self.vm.set_console(console_index=0)
+ self.vm.launch()
+
+ wait_for_console_pattern(self,
+ "The test result is PASS",
+ "The test result is FAIL",
+ vm=None)
+ # no need to gracefully shutdown, just finish
+ self.vm.kill()
+
+ def test_igb(self):
+ """
+ :avocado: tags=device:igb
+ """
+ self.common_test_code("igb")
+
+ def test_igb_nomsi(self):
+ """
+ :avocado: tags=device:igb
+ """
+ self.common_test_code("igb", "pci=nomsi")
+
+ # It seems the other popular cards we model in QEMU currently fail
+ # the pattern test with:
+ #
+ # pattern test failed (reg 0x00178): got 0x00000000 expected 0x00005A5A
+ #
+ # So for now we skip them.
+
+ @skip("Incomplete reg 0x00178 support")
+ def test_e1000(self):
+ """
+ :avocado: tags=device:e1000
+ """
+ self.common_test_code("e1000")
+
+ @skip("Incomplete reg 0x00178 support")
+ def test_i82550(self):
+ """
+ :avocado: tags=device:i82550
+ """
+ self.common_test_code("i82550")
diff --git a/tests/avocado/pc_cpu_hotplug_props.py b/tests/avocado/pc_cpu_hotplug_props.py
new file mode 100644
index 0000000000..4bd3e02665
--- /dev/null
+++ b/tests/avocado/pc_cpu_hotplug_props.py
@@ -0,0 +1,35 @@
+#
+# Ensure CPU die-id can be omitted on -device
+#
+# Copyright (c) 2019 Red Hat Inc
+#
+# Author:
+# Eduardo Habkost <ehabkost@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, see <http://www.gnu.org/licenses/>.
+#
+
+from avocado_qemu import QemuSystemTest
+
+class OmittedCPUProps(QemuSystemTest):
+ """
+ :avocado: tags=arch:x86_64
+ :avocado: tags=cpu:qemu64
+ """
+ def test_no_die_id(self):
+ self.vm.add_args('-nodefaults', '-S')
+ self.vm.add_args('-smp', '1,sockets=2,cores=2,threads=2,maxcpus=8')
+ self.vm.add_args('-device', 'qemu64-x86_64-cpu,socket-id=1,core-id=0,thread-id=0')
+ self.vm.launch()
+ self.assertEqual(len(self.vm.cmd('query-cpus-fast')), 2)
diff --git a/tests/avocado/ppc_405.py b/tests/avocado/ppc_405.py
new file mode 100644
index 0000000000..4e7e01aa76
--- /dev/null
+++ b/tests/avocado/ppc_405.py
@@ -0,0 +1,36 @@
+# Test that the U-Boot firmware boots on ppc 405 machines and check the console
+#
+# Copyright (c) 2021 Red Hat, Inc.
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+from avocado.utils import archive
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+from avocado_qemu import exec_command_and_wait_for_pattern
+
+class Ppc405Machine(QemuSystemTest):
+
+ timeout = 90
+
+ def do_test_ppc405(self):
+ uboot_url = ('https://gitlab.com/huth/u-boot/-/raw/'
+ 'taihu-2021-10-09/u-boot-taihu.bin')
+ uboot_hash = ('3208940e908a5edc7c03eab072c60f0dcfadc2ab');
+ file_path = self.fetch_asset(uboot_url, asset_hash=uboot_hash)
+ self.vm.set_console(console_index=1)
+ self.vm.add_args('-bios', file_path)
+ self.vm.launch()
+ wait_for_console_pattern(self, 'AMCC PPC405EP Evaluation Board')
+ exec_command_and_wait_for_pattern(self, 'reset', 'AMCC PowerPC 405EP')
+
+ def test_ppc_ref405ep(self):
+ """
+ :avocado: tags=arch:ppc
+ :avocado: tags=machine:ref405ep
+ :avocado: tags=cpu:405ep
+ :avocado: tags=accel:tcg
+ """
+ self.require_accelerator("tcg")
+ self.do_test_ppc405()
diff --git a/tests/avocado/ppc_74xx.py b/tests/avocado/ppc_74xx.py
new file mode 100644
index 0000000000..f54757c243
--- /dev/null
+++ b/tests/avocado/ppc_74xx.py
@@ -0,0 +1,136 @@
+# Smoke tests for 74xx cpus (aka G4).
+#
+# Copyright (c) 2021, IBM Corp.
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+
+class ppc74xxCpu(QemuSystemTest):
+ """
+ :avocado: tags=arch:ppc
+ :avocado: tags=accel:tcg
+ """
+ timeout = 5
+
+ def test_ppc_7400(self):
+ """
+ :avocado: tags=cpu:7400
+ """
+ self.require_accelerator("tcg")
+ self.vm.set_console()
+ self.vm.launch()
+ wait_for_console_pattern(self, '>> OpenBIOS')
+ wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+ def test_ppc_7410(self):
+ """
+ :avocado: tags=cpu:7410
+ """
+ self.require_accelerator("tcg")
+ self.vm.set_console()
+ self.vm.launch()
+ wait_for_console_pattern(self, '>> OpenBIOS')
+ wait_for_console_pattern(self, '>> CPU type PowerPC,74xx')
+
+ def test_ppc_7441(self):
+ """
+ :avocado: tags=cpu:7441
+ """
+ self.require_accelerator("tcg")
+ self.vm.set_console()
+ self.vm.launch()
+ wait_for_console_pattern(self, '>> OpenBIOS')
+ wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+ def test_ppc_7445(self):
+ """
+ :avocado: tags=cpu:7445
+ """
+ self.require_accelerator("tcg")
+ self.vm.set_console()
+ self.vm.launch()
+ wait_for_console_pattern(self, '>> OpenBIOS')
+ wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+ def test_ppc_7447(self):
+ """
+ :avocado: tags=cpu:7447
+ """
+ self.require_accelerator("tcg")
+ self.vm.set_console()
+ self.vm.launch()
+ wait_for_console_pattern(self, '>> OpenBIOS')
+ wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+ def test_ppc_7447a(self):
+ """
+ :avocado: tags=cpu:7447a
+ """
+ self.require_accelerator("tcg")
+ self.vm.set_console()
+ self.vm.launch()
+ wait_for_console_pattern(self, '>> OpenBIOS')
+ wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+ def test_ppc_7448(self):
+ """
+ :avocado: tags=cpu:7448
+ """
+ self.require_accelerator("tcg")
+ self.vm.set_console()
+ self.vm.launch()
+ wait_for_console_pattern(self, '>> OpenBIOS')
+ wait_for_console_pattern(self, '>> CPU type PowerPC,MPC86xx')
+
+ def test_ppc_7450(self):
+ """
+ :avocado: tags=cpu:7450
+ """
+ self.require_accelerator("tcg")
+ self.vm.set_console()
+ self.vm.launch()
+ wait_for_console_pattern(self, '>> OpenBIOS')
+ wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+ def test_ppc_7451(self):
+ """
+ :avocado: tags=cpu:7451
+ """
+ self.require_accelerator("tcg")
+ self.vm.set_console()
+ self.vm.launch()
+ wait_for_console_pattern(self, '>> OpenBIOS')
+ wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+ def test_ppc_7455(self):
+ """
+ :avocado: tags=cpu:7455
+ """
+ self.require_accelerator("tcg")
+ self.vm.set_console()
+ self.vm.launch()
+ wait_for_console_pattern(self, '>> OpenBIOS')
+ wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+ def test_ppc_7457(self):
+ """
+ :avocado: tags=cpu:7457
+ """
+ self.require_accelerator("tcg")
+ self.vm.set_console()
+ self.vm.launch()
+ wait_for_console_pattern(self, '>> OpenBIOS')
+ wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+ def test_ppc_7457a(self):
+ """
+ :avocado: tags=cpu:7457a
+ """
+ self.require_accelerator("tcg")
+ self.vm.set_console()
+ self.vm.launch()
+ wait_for_console_pattern(self, '>> OpenBIOS')
+ wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
diff --git a/tests/avocado/ppc_amiga.py b/tests/avocado/ppc_amiga.py
new file mode 100644
index 0000000000..b6f866f91d
--- /dev/null
+++ b/tests/avocado/ppc_amiga.py
@@ -0,0 +1,38 @@
+# Test AmigaNG boards
+#
+# Copyright (c) 2023 BALATON Zoltan
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+from avocado.utils import archive
+from avocado.utils import process
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+
+class AmigaOneMachine(QemuSystemTest):
+
+ timeout = 90
+
+ def test_ppc_amigaone(self):
+ """
+ :avocado: tags=arch:ppc
+ :avocado: tags=machine:amigaone
+ :avocado: tags=device:articia
+ :avocado: tags=accel:tcg
+ """
+ self.require_accelerator("tcg")
+ tar_name = 'A1Firmware_Floppy_05-Mar-2005.zip'
+ tar_url = ('https://www.hyperion-entertainment.com/index.php/'
+ 'downloads?view=download&format=raw&file=25')
+ tar_hash = 'c52e59bc73e31d8bcc3cc2106778f7ac84f6c755'
+ zip_file = self.fetch_asset(tar_name, locations=tar_url,
+ asset_hash=tar_hash)
+ archive.extract(zip_file, self.workdir)
+ cmd = f"tail -c 524288 {self.workdir}/floppy_edition/updater.image >{self.workdir}/u-boot-amigaone.bin"
+ process.run(cmd, shell=True)
+
+ self.vm.set_console()
+ self.vm.add_args('-bios', self.workdir + '/u-boot-amigaone.bin')
+ self.vm.launch()
+ wait_for_console_pattern(self, 'FLASH:')
diff --git a/tests/avocado/ppc_bamboo.py b/tests/avocado/ppc_bamboo.py
new file mode 100644
index 0000000000..a81be3d608
--- /dev/null
+++ b/tests/avocado/ppc_bamboo.py
@@ -0,0 +1,42 @@
+# Test that Linux kernel boots on the ppc bamboo board and check the console
+#
+# Copyright (c) 2021 Red Hat
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+from avocado.utils import archive
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+from avocado_qemu import exec_command_and_wait_for_pattern
+
+class BambooMachine(QemuSystemTest):
+
+ timeout = 90
+
+ def test_ppc_bamboo(self):
+ """
+ :avocado: tags=arch:ppc
+ :avocado: tags=machine:bamboo
+ :avocado: tags=cpu:440epb
+ :avocado: tags=device:rtl8139
+ :avocado: tags=accel:tcg
+ """
+ self.require_accelerator("tcg")
+ self.require_netdev('user')
+ tar_url = ('http://landley.net/aboriginal/downloads/binaries/'
+ 'system-image-powerpc-440fp.tar.gz')
+ tar_hash = '53e5f16414b195b82d2c70272f81c2eedb39bad9'
+ file_path = self.fetch_asset(tar_url, asset_hash=tar_hash)
+ archive.extract(file_path, self.workdir)
+ self.vm.set_console()
+ self.vm.add_args('-kernel', self.workdir +
+ '/system-image-powerpc-440fp/linux',
+ '-initrd', self.workdir +
+ '/system-image-powerpc-440fp/rootfs.cpio.gz',
+ '-nic', 'user,model=rtl8139,restrict=on')
+ self.vm.launch()
+ wait_for_console_pattern(self, 'Type exit when done')
+ exec_command_and_wait_for_pattern(self, 'ping 10.0.2.2',
+ '10.0.2.2 is alive!')
+ exec_command_and_wait_for_pattern(self, 'halt', 'System Halted')
diff --git a/tests/avocado/ppc_hv_tests.py b/tests/avocado/ppc_hv_tests.py
new file mode 100644
index 0000000000..bf8822bb97
--- /dev/null
+++ b/tests/avocado/ppc_hv_tests.py
@@ -0,0 +1,206 @@
+# Tests that specifically try to exercise hypervisor features of the
+# target machines. powernv supports the Power hypervisor ISA, and
+# pseries supports the nested-HV hypervisor spec.
+#
+# Copyright (c) 2023 IBM Corporation
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+from avocado import skipIf, skipUnless
+from avocado.utils import archive
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern, exec_command
+import os
+import time
+import subprocess
+from datetime import datetime
+
+deps = ["xorriso"] # dependent tools needed in the test setup/box.
+
+def which(tool):
+ """ looks up the full path for @tool, returns None if not found
+ or if @tool does not have executable permissions.
+ """
+ paths=os.getenv('PATH')
+ for p in paths.split(os.path.pathsep):
+ p = os.path.join(p, tool)
+ if os.path.exists(p) and os.access(p, os.X_OK):
+ return p
+ return None
+
+def missing_deps():
+ """ returns True if any of the test dependent tools are absent.
+ """
+ for dep in deps:
+ if which(dep) is None:
+ return True
+ return False
+
+# Alpine is a light weight distro that supports QEMU. These tests boot
+# that on the machine then run a QEMU guest inside it in KVM mode,
+# that runs the same Alpine distro image.
+# QEMU packages are downloaded and installed on each test. That's not a
+# large download, but it may be more polite to create qcow2 image with
+# QEMU already installed and use that.
+# XXX: The order of these tests seems to matter, see git blame.
+@skipIf(missing_deps(), 'dependencies (%s) not installed' % ','.join(deps))
+@skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test sometimes gets stuck due to console handling problem')
+@skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited')
+@skipUnless(os.getenv('SPEED') == 'slow', 'runtime limited')
+class HypervisorTest(QemuSystemTest):
+
+ timeout = 1000
+ KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 console=hvc0 '
+ panic_message = 'Kernel panic - not syncing'
+ good_message = 'VFS: Cannot open root device'
+
+ def extract_from_iso(self, iso, path):
+ """
+ Extracts a file from an iso file into the test workdir
+
+ :param iso: path to the iso file
+ :param path: path within the iso file of the file to be extracted
+ :returns: path of the extracted file
+ """
+ filename = os.path.basename(path)
+
+ cwd = os.getcwd()
+ os.chdir(self.workdir)
+
+ with open(filename, "w") as outfile:
+ cmd = "xorriso -osirrox on -indev %s -cpx %s %s" % (iso, path, filename)
+ subprocess.run(cmd.split(),
+ stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+
+ os.chdir(cwd)
+
+ # Return complete path to extracted file. Because callers to
+ # extract_from_iso() specify 'path' with a leading slash, it is
+ # necessary to use os.path.relpath() as otherwise os.path.join()
+ # interprets it as an absolute path and drops the self.workdir part.
+ return os.path.normpath(os.path.join(self.workdir, filename))
+
+ def setUp(self):
+ super().setUp()
+
+ iso_url = ('https://dl-cdn.alpinelinux.org/alpine/v3.18/releases/ppc64le/alpine-standard-3.18.4-ppc64le.iso')
+
+ # Alpine use sha256 so I recalculated this myself
+ iso_sha256 = 'c26b8d3e17c2f3f0fed02b4b1296589c2390e6d5548610099af75300edd7b3ff'
+ iso_path = self.fetch_asset(iso_url, asset_hash=iso_sha256,
+ algorithm = "sha256")
+
+ self.iso_path = iso_path
+ self.vmlinuz = self.extract_from_iso(iso_path, '/boot/vmlinuz-lts')
+ self.initramfs = self.extract_from_iso(iso_path, '/boot/initramfs-lts')
+
+ def do_start_alpine(self):
+ self.vm.set_console()
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE
+ self.vm.add_args("-kernel", self.vmlinuz)
+ self.vm.add_args("-initrd", self.initramfs)
+ self.vm.add_args("-smp", "4", "-m", "2g")
+ self.vm.add_args("-drive", f"file={self.iso_path},format=raw,if=none,id=drive0")
+
+ self.vm.launch()
+ wait_for_console_pattern(self, 'Welcome to Alpine Linux 3.18')
+ exec_command(self, 'root')
+ wait_for_console_pattern(self, 'localhost login:')
+ wait_for_console_pattern(self, 'You may change this message by editing /etc/motd.')
+ # If the time is wrong, SSL certificates can fail.
+ exec_command(self, 'date -s "' + datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S' + '"'))
+ exec_command(self, 'setup-alpine -qe')
+ wait_for_console_pattern(self, 'Updating repository indexes... done.')
+
+ def do_stop_alpine(self):
+ exec_command(self, 'poweroff')
+ wait_for_console_pattern(self, 'alpine:~#')
+ self.vm.wait()
+
+ def do_setup_kvm(self):
+ exec_command(self, 'echo http://dl-cdn.alpinelinux.org/alpine/v3.18/main > /etc/apk/repositories')
+ wait_for_console_pattern(self, 'alpine:~#')
+ exec_command(self, 'echo http://dl-cdn.alpinelinux.org/alpine/v3.18/community >> /etc/apk/repositories')
+ wait_for_console_pattern(self, 'alpine:~#')
+ exec_command(self, 'apk update')
+ wait_for_console_pattern(self, 'alpine:~#')
+ exec_command(self, 'apk add qemu-system-ppc64')
+ wait_for_console_pattern(self, 'alpine:~#')
+ exec_command(self, 'modprobe kvm-hv')
+ wait_for_console_pattern(self, 'alpine:~#')
+
+ # This uses the host's block device as the source file for guest block
+ # device for install media. This is a bit hacky but allows reuse of the
+ # iso without having a passthrough filesystem configured.
+ def do_test_kvm(self, hpt=False):
+ if hpt:
+ append = 'disable_radix'
+ else:
+ append = ''
+ exec_command(self, 'qemu-system-ppc64 -nographic -smp 2 -m 1g '
+ '-machine pseries,x-vof=on,accel=kvm '
+ '-machine cap-cfpc=broken,cap-sbbc=broken,'
+ 'cap-ibs=broken,cap-ccf-assist=off '
+ '-drive file=/dev/nvme0n1,format=raw,readonly=on '
+ '-initrd /media/nvme0n1/boot/initramfs-lts '
+ '-kernel /media/nvme0n1/boot/vmlinuz-lts '
+ '-append \'usbcore.nousb ' + append + '\'')
+ # Alpine 3.18 kernel seems to crash in XHCI USB driver.
+ wait_for_console_pattern(self, 'Welcome to Alpine Linux 3.18')
+ exec_command(self, 'root')
+ wait_for_console_pattern(self, 'localhost login:')
+ wait_for_console_pattern(self, 'You may change this message by editing /etc/motd.')
+ exec_command(self, 'poweroff >& /dev/null')
+ wait_for_console_pattern(self, 'localhost:~#')
+ wait_for_console_pattern(self, 'reboot: Power down')
+ time.sleep(1)
+ exec_command(self, '')
+ wait_for_console_pattern(self, 'alpine:~#')
+
+ def test_hv_pseries(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:pseries
+ :avocado: tags=accel:tcg
+ """
+ self.require_accelerator("tcg")
+ self.vm.add_args("-accel", "tcg,thread=multi")
+ self.vm.add_args('-device', 'nvme,serial=1234,drive=drive0')
+ self.vm.add_args("-machine", "x-vof=on,cap-nested-hv=on")
+ self.do_start_alpine()
+ self.do_setup_kvm()
+ self.do_test_kvm()
+ self.do_stop_alpine()
+
+ def test_hv_pseries_kvm(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:pseries
+ :avocado: tags=accel:kvm
+ """
+ self.require_accelerator("kvm")
+ self.vm.add_args("-accel", "kvm")
+ self.vm.add_args('-device', 'nvme,serial=1234,drive=drive0')
+ self.vm.add_args("-machine", "x-vof=on,cap-nested-hv=on,cap-ccf-assist=off")
+ self.do_start_alpine()
+ self.do_setup_kvm()
+ self.do_test_kvm()
+ self.do_stop_alpine()
+
+ def test_hv_powernv(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:powernv
+ :avocado: tags=accel:tcg
+ """
+ self.require_accelerator("tcg")
+ self.vm.add_args("-accel", "tcg,thread=multi")
+ self.vm.add_args('-device', 'nvme,bus=pcie.2,addr=0x0,serial=1234,drive=drive0',
+ '-device', 'e1000e,netdev=net0,mac=C0:FF:EE:00:00:02,bus=pcie.0,addr=0x0',
+ '-netdev', 'user,id=net0,hostfwd=::20022-:22,hostname=alpine')
+ self.do_start_alpine()
+ self.do_setup_kvm()
+ self.do_test_kvm()
+ self.do_test_kvm(True)
+ self.do_stop_alpine()
diff --git a/tests/avocado/ppc_mpc8544ds.py b/tests/avocado/ppc_mpc8544ds.py
new file mode 100644
index 0000000000..b599fb1cc9
--- /dev/null
+++ b/tests/avocado/ppc_mpc8544ds.py
@@ -0,0 +1,34 @@
+# Test that Linux kernel boots on ppc machines and check the console
+#
+# Copyright (c) 2018, 2020 Red Hat, Inc.
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+from avocado.utils import archive
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+
+class Mpc8544dsMachine(QemuSystemTest):
+
+ timeout = 90
+ KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
+ panic_message = 'Kernel panic - not syncing'
+
+ def test_ppc_mpc8544ds(self):
+ """
+ :avocado: tags=arch:ppc
+ :avocado: tags=machine:mpc8544ds
+ :avocado: tags=accel:tcg
+ """
+ self.require_accelerator("tcg")
+ tar_url = ('https://qemu-advcal.gitlab.io'
+ '/qac-best-of-multiarch/download/day04.tar.xz')
+ tar_hash = 'f46724d281a9f30fa892d458be7beb7d34dc25f9'
+ file_path = self.fetch_asset(tar_url, asset_hash=tar_hash)
+ archive.extract(file_path, self.workdir)
+ self.vm.set_console()
+ self.vm.add_args('-kernel', self.workdir + '/creek/creek.bin')
+ self.vm.launch()
+ wait_for_console_pattern(self, 'QEMU advent calendar 2020',
+ self.panic_message)
diff --git a/tests/avocado/ppc_powernv.py b/tests/avocado/ppc_powernv.py
new file mode 100644
index 0000000000..4342941d5d
--- /dev/null
+++ b/tests/avocado/ppc_powernv.py
@@ -0,0 +1,102 @@
+# Test that Linux kernel boots on ppc powernv machines and check the console
+#
+# Copyright (c) 2018, 2020 Red Hat, Inc.
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+from avocado.utils import archive
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+
+class powernvMachine(QemuSystemTest):
+
+ timeout = 90
+ KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 console=hvc0 '
+ panic_message = 'Kernel panic - not syncing'
+ good_message = 'VFS: Cannot open root device'
+
+ def do_test_linux_boot(self, command_line = KERNEL_COMMON_COMMAND_LINE):
+ self.require_accelerator("tcg")
+ kernel_url = ('https://archives.fedoraproject.org/pub/archive'
+ '/fedora-secondary/releases/29/Everything/ppc64le/os'
+ '/ppc/ppc64/vmlinuz')
+ kernel_hash = '3fe04abfc852b66653b8c3c897a59a689270bc77'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ self.vm.set_console()
+ self.vm.add_args('-kernel', kernel_path,
+ '-append', command_line)
+ self.vm.launch()
+
+ def test_linux_boot(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:powernv
+ :avocado: tags=accel:tcg
+ """
+
+ self.do_test_linux_boot()
+ console_pattern = 'VFS: Cannot open root device'
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+
+ def test_linux_smp_boot(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:powernv
+ :avocado: tags=accel:tcg
+ """
+
+ self.vm.add_args('-smp', '4')
+ self.do_test_linux_boot()
+ console_pattern = 'smp: Brought up 1 node, 4 CPUs'
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+ wait_for_console_pattern(self, self.good_message, self.panic_message)
+
+ def test_linux_smp_hpt_boot(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:powernv
+ :avocado: tags=accel:tcg
+ """
+
+ self.vm.add_args('-smp', '4')
+ self.do_test_linux_boot(self.KERNEL_COMMON_COMMAND_LINE +
+ 'disable_radix')
+ console_pattern = 'smp: Brought up 1 node, 4 CPUs'
+ wait_for_console_pattern(self, 'hash-mmu: Initializing hash mmu',
+ self.panic_message)
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+ wait_for_console_pattern(self, self.good_message, self.panic_message)
+
+ def test_linux_smt_boot(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:powernv
+ :avocado: tags=accel:tcg
+ """
+
+ self.vm.add_args('-smp', '4,threads=4')
+ self.do_test_linux_boot()
+ console_pattern = 'CPU maps initialized for 4 threads per core'
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+ console_pattern = 'smp: Brought up 1 node, 4 CPUs'
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+ wait_for_console_pattern(self, self.good_message, self.panic_message)
+
+ def test_linux_big_boot(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:powernv
+ :avocado: tags=accel:tcg
+ """
+
+ self.vm.add_args('-smp', '16,threads=4,cores=2,sockets=2')
+
+ # powernv does not support NUMA
+ self.do_test_linux_boot()
+ console_pattern = 'CPU maps initialized for 4 threads per core'
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+ console_pattern = 'smp: Brought up 2 nodes, 16 CPUs'
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+ wait_for_console_pattern(self, self.good_message, self.panic_message)
diff --git a/tests/avocado/ppc_prep_40p.py b/tests/avocado/ppc_prep_40p.py
new file mode 100644
index 0000000000..d4f1eb7e1d
--- /dev/null
+++ b/tests/avocado/ppc_prep_40p.py
@@ -0,0 +1,85 @@
+# Functional test that boots a PReP/40p machine and checks its serial console.
+#
+# Copyright (c) Philippe Mathieu-Daudé <f4bug@amsat.org>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import os
+
+from avocado import skipUnless
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+
+
+class IbmPrep40pMachine(QemuSystemTest):
+
+ timeout = 60
+
+ # 12H0455 PPS Firmware Licensed Materials
+ # Property of IBM (C) Copyright IBM Corp. 1994.
+ # All rights reserved.
+ # U.S. Government Users Restricted Rights - Use, duplication or disclosure
+ # restricted by GSA ADP Schedule Contract with IBM Corp.
+ @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code')
+ def test_factory_firmware_and_netbsd(self):
+ """
+ :avocado: tags=arch:ppc
+ :avocado: tags=machine:40p
+ :avocado: tags=os:netbsd
+ :avocado: tags=slowness:high
+ :avocado: tags=accel:tcg
+ """
+ self.require_accelerator("tcg")
+ bios_url = ('http://ftpmirror.your.org/pub/misc/'
+ 'ftp.software.ibm.com/rs6000/firmware/'
+ '7020-40p/P12H0456.IMG')
+ bios_hash = '1775face4e6dc27f3a6ed955ef6eb331bf817f03'
+ bios_path = self.fetch_asset(bios_url, asset_hash=bios_hash)
+ drive_url = ('https://archive.netbsd.org/pub/NetBSD-archive/'
+ 'NetBSD-4.0/prep/installation/floppy/generic_com0.fs')
+ drive_hash = 'dbcfc09912e71bd5f0d82c7c1ee43082fb596ceb'
+ drive_path = self.fetch_asset(drive_url, asset_hash=drive_hash)
+
+ self.vm.set_console()
+ self.vm.add_args('-bios', bios_path,
+ '-fda', drive_path)
+ self.vm.launch()
+ os_banner = 'NetBSD 4.0 (GENERIC) #0: Sun Dec 16 00:49:40 PST 2007'
+ wait_for_console_pattern(self, os_banner)
+ wait_for_console_pattern(self, 'Model: IBM PPS Model 6015')
+
+ def test_openbios_192m(self):
+ """
+ :avocado: tags=arch:ppc
+ :avocado: tags=machine:40p
+ :avocado: tags=accel:tcg
+ """
+ self.require_accelerator("tcg")
+ self.vm.set_console()
+ self.vm.add_args('-m', '192') # test fw_cfg
+
+ self.vm.launch()
+ wait_for_console_pattern(self, '>> OpenBIOS')
+ wait_for_console_pattern(self, '>> Memory: 192M')
+ wait_for_console_pattern(self, '>> CPU type PowerPC,604')
+
+ def test_openbios_and_netbsd(self):
+ """
+ :avocado: tags=arch:ppc
+ :avocado: tags=machine:40p
+ :avocado: tags=os:netbsd
+ :avocado: tags=accel:tcg
+ """
+ self.require_accelerator("tcg")
+ drive_url = ('https://archive.netbsd.org/pub/NetBSD-archive/'
+ 'NetBSD-7.1.2/iso/NetBSD-7.1.2-prep.iso')
+ drive_hash = 'ac6fa2707d888b36d6fa64de6e7fe48e'
+ drive_path = self.fetch_asset(drive_url, asset_hash=drive_hash,
+ algorithm='md5')
+ self.vm.set_console()
+ self.vm.add_args('-cdrom', drive_path,
+ '-boot', 'd')
+
+ self.vm.launch()
+ wait_for_console_pattern(self, 'NetBSD/prep BOOT, Revision 1.9')
diff --git a/tests/avocado/ppc_pseries.py b/tests/avocado/ppc_pseries.py
new file mode 100644
index 0000000000..74aaa4ac4a
--- /dev/null
+++ b/tests/avocado/ppc_pseries.py
@@ -0,0 +1,110 @@
+# Test that Linux kernel boots on ppc machines and check the console
+#
+# Copyright (c) 2018, 2020 Red Hat, Inc.
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+from avocado.utils import archive
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+
+class pseriesMachine(QemuSystemTest):
+
+ timeout = 90
+ KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 console=hvc0 '
+ panic_message = 'Kernel panic - not syncing'
+ good_message = 'VFS: Cannot open root device'
+
+ def do_test_ppc64_linux_boot(self, kernel_command_line = KERNEL_COMMON_COMMAND_LINE):
+ kernel_url = ('https://archives.fedoraproject.org/pub/archive'
+ '/fedora-secondary/releases/29/Everything/ppc64le/os'
+ '/ppc/ppc64/vmlinuz')
+ kernel_hash = '3fe04abfc852b66653b8c3c897a59a689270bc77'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ self.vm.set_console()
+ self.vm.add_args('-kernel', kernel_path,
+ '-append', kernel_command_line)
+ self.vm.launch()
+
+ def test_ppc64_vof_linux_boot(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:pseries
+ """
+
+ self.vm.add_args('-machine', 'x-vof=on')
+ self.do_test_ppc64_linux_boot()
+ console_pattern = 'VFS: Cannot open root device'
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+
+ def test_ppc64_linux_boot(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:pseries
+ """
+
+ self.do_test_ppc64_linux_boot()
+ console_pattern = 'VFS: Cannot open root device'
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+
+ def test_ppc64_linux_smp_boot(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:pseries
+ """
+
+ self.vm.add_args('-smp', '4')
+ self.do_test_ppc64_linux_boot()
+ console_pattern = 'smp: Brought up 1 node, 4 CPUs'
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+ wait_for_console_pattern(self, self.good_message, self.panic_message)
+
+ def test_ppc64_linux_hpt_smp_boot(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:pseries
+ """
+
+ self.vm.add_args('-smp', '4')
+ self.do_test_ppc64_linux_boot(self.KERNEL_COMMON_COMMAND_LINE +
+ 'disable_radix')
+ console_pattern = 'smp: Brought up 1 node, 4 CPUs'
+ wait_for_console_pattern(self, 'hash-mmu: Initializing hash mmu',
+ self.panic_message)
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+ wait_for_console_pattern(self, self.good_message, self.panic_message)
+
+ def test_ppc64_linux_smt_boot(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:pseries
+ """
+
+ self.vm.add_args('-smp', '4,threads=4')
+ self.do_test_ppc64_linux_boot()
+ console_pattern = 'CPU maps initialized for 4 threads per core'
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+ console_pattern = 'smp: Brought up 1 node, 4 CPUs'
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+ wait_for_console_pattern(self, self.good_message, self.panic_message)
+
+ def test_ppc64_linux_big_boot(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:pseries
+ """
+
+ self.vm.add_args('-smp', '16,threads=4,cores=2,sockets=2')
+ self.vm.add_args('-m', '512M',
+ '-object', 'memory-backend-ram,size=256M,id=m0',
+ '-object', 'memory-backend-ram,size=256M,id=m1')
+ self.vm.add_args('-numa', 'node,nodeid=0,memdev=m0')
+ self.vm.add_args('-numa', 'node,nodeid=1,memdev=m1')
+ self.do_test_ppc64_linux_boot()
+ console_pattern = 'CPU maps initialized for 4 threads per core'
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+ console_pattern = 'smp: Brought up 2 nodes, 16 CPUs'
+ wait_for_console_pattern(self, console_pattern, self.panic_message)
+ wait_for_console_pattern(self, self.good_message, self.panic_message)
diff --git a/tests/avocado/ppc_virtex_ml507.py b/tests/avocado/ppc_virtex_ml507.py
new file mode 100644
index 0000000000..a73f8ae396
--- /dev/null
+++ b/tests/avocado/ppc_virtex_ml507.py
@@ -0,0 +1,36 @@
+# Test that Linux kernel boots on ppc machines and check the console
+#
+# Copyright (c) 2018, 2020 Red Hat, Inc.
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+from avocado.utils import archive
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+
+class VirtexMl507Machine(QemuSystemTest):
+
+ timeout = 90
+ KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
+ panic_message = 'Kernel panic - not syncing'
+
+ def test_ppc_virtex_ml507(self):
+ """
+ :avocado: tags=arch:ppc
+ :avocado: tags=machine:virtex-ml507
+ :avocado: tags=accel:tcg
+ """
+ self.require_accelerator("tcg")
+ tar_url = ('https://qemu-advcal.gitlab.io'
+ '/qac-best-of-multiarch/download/day08.tar.xz')
+ tar_hash = '74c68f5af7a7b8f21c03097b298f3bb77ff52c1f'
+ file_path = self.fetch_asset(tar_url, asset_hash=tar_hash)
+ archive.extract(file_path, self.workdir)
+ self.vm.set_console()
+ self.vm.add_args('-kernel', self.workdir + '/hippo/hippo.linux',
+ '-dtb', self.workdir + '/hippo/virtex440-ml507.dtb',
+ '-m', '512')
+ self.vm.launch()
+ wait_for_console_pattern(self, 'QEMU advent calendar 2020',
+ self.panic_message)
diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py
new file mode 100644
index 0000000000..2c81412dba
--- /dev/null
+++ b/tests/avocado/replay_kernel.py
@@ -0,0 +1,550 @@
+# Record/replay test that boots a Linux kernel
+#
+# Copyright (c) 2020 ISP RAS
+#
+# Author:
+# Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import os
+import lzma
+import shutil
+import logging
+import time
+
+from avocado import skip
+from avocado import skipUnless
+from avocado import skipUnless
+from avocado_qemu import wait_for_console_pattern
+from avocado.utils import archive
+from avocado.utils import process
+from boot_linux_console import LinuxKernelTest
+
+class ReplayKernelBase(LinuxKernelTest):
+ """
+ Boots a Linux kernel in record mode and checks that the console
+ is operational and the kernel command line is properly passed
+ from QEMU to the kernel.
+ Then replays the same scenario and verifies, that QEMU correctly
+ terminates.
+ """
+
+ timeout = 120
+ KERNEL_COMMON_COMMAND_LINE = 'printk.time=1 panic=-1 '
+
+ def run_vm(self, kernel_path, kernel_command_line, console_pattern,
+ record, shift, args, replay_path):
+ # icount requires TCG to be available
+ self.require_accelerator('tcg')
+
+ logger = logging.getLogger('replay')
+ start_time = time.time()
+ vm = self.get_vm()
+ vm.set_console()
+ if record:
+ logger.info('recording the execution...')
+ mode = 'record'
+ else:
+ logger.info('replaying the execution...')
+ mode = 'replay'
+ vm.add_args('-icount', 'shift=%s,rr=%s,rrfile=%s' %
+ (shift, mode, replay_path),
+ '-kernel', kernel_path,
+ '-append', kernel_command_line,
+ '-net', 'none',
+ '-no-reboot')
+ if args:
+ vm.add_args(*args)
+ vm.launch()
+ self.wait_for_console_pattern(console_pattern, vm)
+ if record:
+ vm.shutdown()
+ logger.info('finished the recording with log size %s bytes'
+ % os.path.getsize(replay_path))
+ else:
+ vm.wait()
+ logger.info('successfully finished the replay')
+ elapsed = time.time() - start_time
+ logger.info('elapsed time %.2f sec' % elapsed)
+ return elapsed
+
+ def run_rr(self, kernel_path, kernel_command_line, console_pattern,
+ shift=7, args=None):
+ replay_path = os.path.join(self.workdir, 'replay.bin')
+ t1 = self.run_vm(kernel_path, kernel_command_line, console_pattern,
+ True, shift, args, replay_path)
+ t2 = self.run_vm(kernel_path, kernel_command_line, console_pattern,
+ False, shift, args, replay_path)
+ logger = logging.getLogger('replay')
+ logger.info('replay overhead {:.2%}'.format(t2 / t1 - 1))
+
+class ReplayKernelNormal(ReplayKernelBase):
+
+ def test_i386_pc(self):
+ """
+ :avocado: tags=arch:i386
+ :avocado: tags=machine:pc
+ """
+ kernel_url = ('https://storage.tuxboot.com/20230331/i386/bzImage')
+ kernel_hash = 'a3e5b32a354729e65910f5a1ffcda7c14a6c12a55e8213fb86e277f1b76ed956'
+ kernel_path = self.fetch_asset(kernel_url,
+ asset_hash=kernel_hash,
+ algorithm = "sha256")
+
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0'
+ console_pattern = 'VFS: Cannot open root device'
+
+ self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5)
+
+ # See https://gitlab.com/qemu-project/qemu/-/issues/2094
+ @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test sometimes gets stuck')
+ def test_x86_64_pc(self):
+ """
+ :avocado: tags=arch:x86_64
+ :avocado: tags=machine:pc
+ :avocado: tags=flaky
+ """
+ kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora'
+ '/linux/releases/29/Everything/x86_64/os/images/pxeboot'
+ '/vmlinuz')
+ kernel_hash = '23bebd2680757891cf7adedb033532163a792495'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0'
+ console_pattern = 'VFS: Cannot open root device'
+
+ self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5)
+
+ def test_mips_malta(self):
+ """
+ :avocado: tags=arch:mips
+ :avocado: tags=machine:malta
+ :avocado: tags=endian:big
+ """
+ deb_url = ('http://snapshot.debian.org/archive/debian/'
+ '20130217T032700Z/pool/main/l/linux-2.6/'
+ 'linux-image-2.6.32-5-4kc-malta_2.6.32-48_mips.deb')
+ deb_hash = 'a8cfc28ad8f45f54811fc6cf74fc43ffcfe0ba04'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path,
+ '/boot/vmlinux-2.6.32-5-4kc-malta')
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0'
+ console_pattern = 'Kernel command line: %s' % kernel_command_line
+
+ self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5)
+
+ def test_mips64el_malta(self):
+ """
+ This test requires the ar tool to extract "data.tar.gz" from
+ the Debian package.
+
+ The kernel can be rebuilt using this Debian kernel source [1] and
+ following the instructions on [2].
+
+ [1] http://snapshot.debian.org/package/linux-2.6/2.6.32-48/
+ #linux-source-2.6.32_2.6.32-48
+ [2] https://kernel-team.pages.debian.net/kernel-handbook/
+ ch-common-tasks.html#s-common-official
+
+ :avocado: tags=arch:mips64el
+ :avocado: tags=machine:malta
+ """
+ deb_url = ('http://snapshot.debian.org/archive/debian/'
+ '20130217T032700Z/pool/main/l/linux-2.6/'
+ 'linux-image-2.6.32-5-5kc-malta_2.6.32-48_mipsel.deb')
+ deb_hash = '1aaec92083bf22fda31e0d27fa8d9a388e5fc3d5'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path,
+ '/boot/vmlinux-2.6.32-5-5kc-malta')
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0'
+ console_pattern = 'Kernel command line: %s' % kernel_command_line
+ self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5)
+
+ def test_aarch64_virt(self):
+ """
+ :avocado: tags=arch:aarch64
+ :avocado: tags=machine:virt
+ :avocado: tags=cpu:cortex-a53
+ """
+ kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora'
+ '/linux/releases/29/Everything/aarch64/os/images/pxeboot'
+ '/vmlinuz')
+ kernel_hash = '8c73e469fc6ea06a58dc83a628fc695b693b8493'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyAMA0')
+ console_pattern = 'VFS: Cannot open root device'
+
+ self.run_rr(kernel_path, kernel_command_line, console_pattern)
+
+ def test_arm_virt(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:virt
+ """
+ kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora'
+ '/linux/releases/29/Everything/armhfp/os/images/pxeboot'
+ '/vmlinuz')
+ kernel_hash = 'e9826d741b4fb04cadba8d4824d1ed3b7fb8b4d4'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyAMA0')
+ console_pattern = 'VFS: Cannot open root device'
+
+ self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=1)
+
+ def test_arm_cubieboard_initrd(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:cubieboard
+ """
+ deb_url = ('https://apt.armbian.com/pool/main/l/'
+ 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb')
+ deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path,
+ '/boot/vmlinuz-5.10.16-sunxi')
+ dtb_path = '/usr/lib/linux-image-current-sunxi/sun4i-a10-cubieboard.dtb'
+ dtb_path = self.extract_from_deb(deb_path, dtb_path)
+ initrd_url = ('https://github.com/groeck/linux-build-test/raw/'
+ '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/'
+ 'arm/rootfs-armv5.cpio.gz')
+ initrd_hash = '2b50f1873e113523967806f4da2afe385462ff9b'
+ initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
+ initrd_path = os.path.join(self.workdir, 'rootfs.cpio')
+ archive.gzip_uncompress(initrd_path_gz, initrd_path)
+
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyS0,115200 '
+ 'usbcore.nousb '
+ 'panic=-1 noreboot')
+ console_pattern = 'Boot successful.'
+ self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=1,
+ args=('-dtb', dtb_path,
+ '-initrd', initrd_path,
+ '-no-reboot'))
+
+ def test_s390x_s390_ccw_virtio(self):
+ """
+ :avocado: tags=arch:s390x
+ :avocado: tags=machine:s390-ccw-virtio
+ """
+ kernel_url = ('https://archives.fedoraproject.org/pub/archive'
+ '/fedora-secondary/releases/29/Everything/s390x/os/images'
+ '/kernel.img')
+ kernel_hash = 'e8e8439103ef8053418ef062644ffd46a7919313'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=sclp0'
+ console_pattern = 'Kernel command line: %s' % kernel_command_line
+ self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=9)
+
+ def test_alpha_clipper(self):
+ """
+ :avocado: tags=arch:alpha
+ :avocado: tags=machine:clipper
+ """
+ kernel_url = ('http://archive.debian.org/debian/dists/lenny/main/'
+ 'installer-alpha/20090123lenny10/images/cdrom/vmlinuz')
+ kernel_hash = '3a943149335529e2ed3e74d0d787b85fb5671ba3'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ uncompressed_kernel = archive.uncompress(kernel_path, self.workdir)
+
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0'
+ console_pattern = 'Kernel command line: %s' % kernel_command_line
+ self.run_rr(uncompressed_kernel, kernel_command_line, console_pattern, shift=9,
+ args=('-nodefaults', ))
+
+ def test_ppc64_pseries(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:pseries
+ :avocado: tags=accel:tcg
+ """
+ kernel_url = ('https://archives.fedoraproject.org/pub/archive'
+ '/fedora-secondary/releases/29/Everything/ppc64le/os'
+ '/ppc/ppc64/vmlinuz')
+ kernel_hash = '3fe04abfc852b66653b8c3c897a59a689270bc77'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=hvc0'
+ console_pattern = 'VFS: Cannot open root device'
+ self.run_rr(kernel_path, kernel_command_line, console_pattern)
+
+ def test_ppc64_powernv(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:powernv
+ :avocado: tags=accel:tcg
+ """
+ kernel_url = ('https://archives.fedoraproject.org/pub/archive'
+ '/fedora-secondary/releases/29/Everything/ppc64le/os'
+ '/ppc/ppc64/vmlinuz')
+ kernel_hash = '3fe04abfc852b66653b8c3c897a59a689270bc77'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + \
+ 'console=tty0 console=hvc0'
+ console_pattern = 'VFS: Cannot open root device'
+ self.run_rr(kernel_path, kernel_command_line, console_pattern)
+
+ def test_m68k_q800(self):
+ """
+ :avocado: tags=arch:m68k
+ :avocado: tags=machine:q800
+ """
+ deb_url = ('https://snapshot.debian.org/archive/debian-ports'
+ '/20191021T083923Z/pool-m68k/main'
+ '/l/linux/kernel-image-5.3.0-1-m68k-di_5.3.7-1_m68k.udeb')
+ deb_hash = '044954bb9be4160a3ce81f8bc1b5e856b75cccd1'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path,
+ '/boot/vmlinux-5.3.0-1-m68k')
+
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyS0 vga=off')
+ console_pattern = 'No filesystem could mount root'
+ self.run_rr(kernel_path, kernel_command_line, console_pattern)
+
+ def do_test_advcal_2018(self, file_path, kernel_name, args=None):
+ archive.extract(file_path, self.workdir)
+
+ for entry in os.scandir(self.workdir):
+ if entry.name.startswith('day') and entry.is_dir():
+ kernel_path = os.path.join(entry.path, kernel_name)
+ break
+
+ kernel_command_line = ''
+ console_pattern = 'QEMU advent calendar'
+ self.run_rr(kernel_path, kernel_command_line, console_pattern,
+ args=args)
+
+ def test_arm_vexpressa9(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=machine:vexpress-a9
+ """
+ tar_hash = '32b7677ce8b6f1471fb0059865f451169934245b'
+ tar_url = ('https://qemu-advcal.gitlab.io'
+ '/qac-best-of-multiarch/download/day16.tar.xz')
+ file_path = self.fetch_asset(tar_url, asset_hash=tar_hash)
+ dtb_path = self.workdir + '/day16/vexpress-v2p-ca9.dtb'
+ self.do_test_advcal_2018(file_path, 'winter.zImage',
+ args=('-dtb', dtb_path))
+
+ def test_m68k_mcf5208evb(self):
+ """
+ :avocado: tags=arch:m68k
+ :avocado: tags=machine:mcf5208evb
+ """
+ tar_hash = 'ac688fd00561a2b6ce1359f9ff6aa2b98c9a570c'
+ tar_url = ('https://qemu-advcal.gitlab.io'
+ '/qac-best-of-multiarch/download/day07.tar.xz')
+ file_path = self.fetch_asset(tar_url, asset_hash=tar_hash)
+ self.do_test_advcal_2018(file_path, 'sanity-clause.elf')
+
+ def test_microblaze_s3adsp1800(self):
+ """
+ :avocado: tags=arch:microblaze
+ :avocado: tags=machine:petalogix-s3adsp1800
+ """
+ tar_hash = '08bf3e3bfb6b6c7ce1e54ab65d54e189f2caf13f'
+ tar_url = ('https://qemu-advcal.gitlab.io'
+ '/qac-best-of-multiarch/download/day17.tar.xz')
+ file_path = self.fetch_asset(tar_url, asset_hash=tar_hash)
+ self.do_test_advcal_2018(file_path, 'ballerina.bin')
+
+ def test_ppc64_e500(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:ppce500
+ :avocado: tags=cpu:e5500
+ """
+ tar_hash = '6951d86d644b302898da2fd701739c9406527fe1'
+ tar_url = ('https://qemu-advcal.gitlab.io'
+ '/qac-best-of-multiarch/download/day19.tar.xz')
+ file_path = self.fetch_asset(tar_url, asset_hash=tar_hash)
+ self.do_test_advcal_2018(file_path, 'uImage')
+
+ def test_or1k_sim(self):
+ """
+ :avocado: tags=arch:or1k
+ :avocado: tags=machine:or1k-sim
+ """
+ tar_hash = '20334cdaf386108c530ff0badaecc955693027dd'
+ tar_url = ('https://qemu-advcal.gitlab.io'
+ '/qac-best-of-multiarch/download/day20.tar.xz')
+ file_path = self.fetch_asset(tar_url, asset_hash=tar_hash)
+ self.do_test_advcal_2018(file_path, 'vmlinux')
+
+ def test_ppc_g3beige(self):
+ """
+ :avocado: tags=arch:ppc
+ :avocado: tags=machine:g3beige
+ """
+ tar_hash = 'e0b872a5eb8fdc5bed19bd43ffe863900ebcedfc'
+ tar_url = ('https://qemu-advcal.gitlab.io'
+ '/qac-best-of-multiarch/download/day15.tar.xz')
+ file_path = self.fetch_asset(tar_url, asset_hash=tar_hash)
+ self.do_test_advcal_2018(file_path, 'invaders.elf',
+ args=('-M', 'graphics=off'))
+
+ def test_ppc_mac99(self):
+ """
+ :avocado: tags=arch:ppc
+ :avocado: tags=machine:mac99
+ """
+ tar_hash = 'e0b872a5eb8fdc5bed19bd43ffe863900ebcedfc'
+ tar_url = ('https://qemu-advcal.gitlab.io'
+ '/qac-best-of-multiarch/download/day15.tar.xz')
+ file_path = self.fetch_asset(tar_url, asset_hash=tar_hash)
+ self.do_test_advcal_2018(file_path, 'invaders.elf',
+ args=('-M', 'graphics=off'))
+
+ def test_sparc_ss20(self):
+ """
+ :avocado: tags=arch:sparc
+ :avocado: tags=machine:SS-20
+ """
+ tar_hash = 'b18550d5d61c7615d989a06edace051017726a9f'
+ tar_url = ('https://qemu-advcal.gitlab.io'
+ '/qac-best-of-multiarch/download/day11.tar.xz')
+ file_path = self.fetch_asset(tar_url, asset_hash=tar_hash)
+ self.do_test_advcal_2018(file_path, 'zImage.elf')
+
+ def test_xtensa_lx60(self):
+ """
+ :avocado: tags=arch:xtensa
+ :avocado: tags=machine:lx60
+ :avocado: tags=cpu:dc233c
+ """
+ tar_hash = '49e88d9933742f0164b60839886c9739cb7a0d34'
+ tar_url = ('https://qemu-advcal.gitlab.io'
+ '/qac-best-of-multiarch/download/day02.tar.xz')
+ file_path = self.fetch_asset(tar_url, asset_hash=tar_hash)
+ self.do_test_advcal_2018(file_path, 'santas-sleigh-ride.elf')
+
+@skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout')
+class ReplayKernelSlow(ReplayKernelBase):
+ # Override the timeout, because this kernel includes an inner
+ # loop which is executed with TB recompilings during replay,
+ # making it very slow.
+ timeout = 180
+
+ def test_mips_malta_cpio(self):
+ """
+ :avocado: tags=arch:mips
+ :avocado: tags=machine:malta
+ :avocado: tags=endian:big
+ :avocado: tags=slowness:high
+ """
+ deb_url = ('http://snapshot.debian.org/archive/debian/'
+ '20160601T041800Z/pool/main/l/linux/'
+ 'linux-image-4.5.0-2-4kc-malta_4.5.5-1_mips.deb')
+ deb_hash = 'a3c84f3e88b54e06107d65a410d1d1e8e0f340f8'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path,
+ '/boot/vmlinux-4.5.0-2-4kc-malta')
+ initrd_url = ('https://github.com/groeck/linux-build-test/raw/'
+ '8584a59ed9e5eb5ee7ca91f6d74bbb06619205b8/rootfs/'
+ 'mips/rootfs.cpio.gz')
+ initrd_hash = 'bf806e17009360a866bf537f6de66590de349a99'
+ initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
+ initrd_path = self.workdir + "rootfs.cpio"
+ archive.gzip_uncompress(initrd_path_gz, initrd_path)
+
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyS0 console=tty '
+ 'rdinit=/sbin/init noreboot')
+ console_pattern = 'Boot successful.'
+ self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5,
+ args=('-initrd', initrd_path))
+
+ @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code')
+ def test_mips64el_malta_5KEc_cpio(self):
+ """
+ :avocado: tags=arch:mips64el
+ :avocado: tags=machine:malta
+ :avocado: tags=endian:little
+ :avocado: tags=slowness:high
+ :avocado: tags=cpu:5KEc
+ """
+ kernel_url = ('https://github.com/philmd/qemu-testing-blob/'
+ 'raw/9ad2df38/mips/malta/mips64el/'
+ 'vmlinux-3.19.3.mtoman.20150408')
+ kernel_hash = '00d1d268fb9f7d8beda1de6bebcc46e884d71754'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+ initrd_url = ('https://github.com/groeck/linux-build-test/'
+ 'raw/8584a59e/rootfs/'
+ 'mipsel64/rootfs.mipsel64r1.cpio.gz')
+ initrd_hash = '1dbb8a396e916847325284dbe2151167'
+ initrd_path_gz = self.fetch_asset(initrd_url, algorithm='md5',
+ asset_hash=initrd_hash)
+ initrd_path = self.workdir + "rootfs.cpio"
+ archive.gzip_uncompress(initrd_path_gz, initrd_path)
+
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyS0 console=tty '
+ 'rdinit=/sbin/init noreboot')
+ console_pattern = 'Boot successful.'
+ self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5,
+ args=('-initrd', initrd_path))
+
+ def do_test_mips_malta32el_nanomips(self, kernel_path_xz):
+ kernel_path = self.workdir + "kernel"
+ with lzma.open(kernel_path_xz, 'rb') as f_in:
+ with open(kernel_path, 'wb') as f_out:
+ shutil.copyfileobj(f_in, f_out)
+
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'mem=256m@@0x0 '
+ 'console=ttyS0')
+ console_pattern = 'Kernel command line: %s' % kernel_command_line
+ self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5)
+
+ def test_mips_malta32el_nanomips_4k(self):
+ """
+ :avocado: tags=arch:mipsel
+ :avocado: tags=machine:malta
+ :avocado: tags=endian:little
+ :avocado: tags=cpu:I7200
+ """
+ kernel_url = ('http://mipsdistros.mips.com/LinuxDistro/nanomips/'
+ 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/'
+ 'generic_nano32r6el_page4k.xz')
+ kernel_hash = '477456aafd2a0f1ddc9482727f20fe9575565dd6'
+ kernel_path_xz = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+ self.do_test_mips_malta32el_nanomips(kernel_path_xz)
+
+ def test_mips_malta32el_nanomips_16k_up(self):
+ """
+ :avocado: tags=arch:mipsel
+ :avocado: tags=machine:malta
+ :avocado: tags=endian:little
+ :avocado: tags=cpu:I7200
+ """
+ kernel_url = ('http://mipsdistros.mips.com/LinuxDistro/nanomips/'
+ 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/'
+ 'generic_nano32r6el_page16k_up.xz')
+ kernel_hash = 'e882868f944c71c816e832e2303b7874d044a7bc'
+ kernel_path_xz = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+ self.do_test_mips_malta32el_nanomips(kernel_path_xz)
+
+ def test_mips_malta32el_nanomips_64k_dbg(self):
+ """
+ :avocado: tags=arch:mipsel
+ :avocado: tags=machine:malta
+ :avocado: tags=endian:little
+ :avocado: tags=cpu:I7200
+ """
+ kernel_url = ('http://mipsdistros.mips.com/LinuxDistro/nanomips/'
+ 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/'
+ 'generic_nano32r6el_page64k_dbg.xz')
+ kernel_hash = '18d1c68f2e23429e266ca39ba5349ccd0aeb7180'
+ kernel_path_xz = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+ self.do_test_mips_malta32el_nanomips(kernel_path_xz)
diff --git a/tests/avocado/replay_linux.py b/tests/avocado/replay_linux.py
new file mode 100644
index 0000000000..f3a43dc98c
--- /dev/null
+++ b/tests/avocado/replay_linux.py
@@ -0,0 +1,196 @@
+# Record/replay test that boots a complete Linux system via a cloud image
+#
+# Copyright (c) 2020 ISP RAS
+#
+# Author:
+# Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import os
+import logging
+import time
+
+from avocado import skipUnless
+from avocado_qemu import BUILD_DIR
+from avocado.utils import cloudinit
+from avocado.utils import network
+from avocado.utils import vmimage
+from avocado.utils import datadrainer
+from avocado.utils.path import find_command
+from avocado_qemu import LinuxTest
+
+class ReplayLinux(LinuxTest):
+ """
+ Boots a Linux system, checking for a successful initialization
+ """
+
+ timeout = 1800
+ chksum = None
+ hdd = 'ide-hd'
+ cd = 'ide-cd'
+ bus = 'ide'
+
+ def setUp(self):
+ # LinuxTest does many replay-incompatible things, but includes
+ # useful methods. Do not setup LinuxTest here and just
+ # call some functions.
+ super(LinuxTest, self).setUp()
+ self._set_distro()
+ self.boot_path = self.download_boot()
+ self.phone_server = cloudinit.PhoneHomeServer(('0.0.0.0', 0),
+ self.name)
+ ssh_pubkey, self.ssh_key = self.set_up_existing_ssh_keys()
+ self.cloudinit_path = self.prepare_cloudinit(ssh_pubkey)
+
+ def vm_add_disk(self, vm, path, id, device):
+ bus_string = ''
+ if self.bus:
+ bus_string = ',bus=%s.%d' % (self.bus, id,)
+ vm.add_args('-drive', 'file=%s,snapshot=on,id=disk%s,if=none' % (path, id))
+ vm.add_args('-drive',
+ 'driver=blkreplay,id=disk%s-rr,if=none,image=disk%s' % (id, id))
+ vm.add_args('-device',
+ '%s,drive=disk%s-rr%s' % (device, id, bus_string))
+
+ def vm_add_cdrom(self, vm, path, id, device):
+ vm.add_args('-drive', 'file=%s,id=disk%s,if=none,media=cdrom' % (path, id))
+
+ def launch_and_wait(self, record, args, shift):
+ self.require_netdev('user')
+ vm = self.get_vm()
+ vm.add_args('-smp', '1')
+ vm.add_args('-m', '1024')
+ vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22',
+ '-device', 'virtio-net,netdev=vnet')
+ vm.add_args('-object', 'filter-replay,id=replay,netdev=vnet')
+ if args:
+ vm.add_args(*args)
+ self.vm_add_disk(vm, self.boot_path, 0, self.hdd)
+ self.vm_add_cdrom(vm, self.cloudinit_path, 1, self.cd)
+ logger = logging.getLogger('replay')
+ if record:
+ logger.info('recording the execution...')
+ mode = 'record'
+ else:
+ logger.info('replaying the execution...')
+ mode = 'replay'
+ replay_path = os.path.join(self.workdir, 'replay.bin')
+ vm.add_args('-icount', 'shift=%s,rr=%s,rrfile=%s' %
+ (shift, mode, replay_path))
+
+ start_time = time.time()
+
+ vm.set_console()
+ vm.launch()
+ console_drainer = datadrainer.LineLogger(vm.console_socket.fileno(),
+ logger=self.log.getChild('console'),
+ stop_check=(lambda : not vm.is_running()))
+ console_drainer.start()
+ if record:
+ while not self.phone_server.instance_phoned_back:
+ self.phone_server.handle_request()
+ vm.shutdown()
+ logger.info('finished the recording with log size %s bytes'
+ % os.path.getsize(replay_path))
+ else:
+ vm.event_wait('SHUTDOWN', self.timeout)
+ vm.wait()
+ logger.info('successfully finished the replay')
+ elapsed = time.time() - start_time
+ logger.info('elapsed time %.2f sec' % elapsed)
+ return elapsed
+
+ def run_rr(self, args=None, shift=7):
+ t1 = self.launch_and_wait(True, args, shift)
+ t2 = self.launch_and_wait(False, args, shift)
+ logger = logging.getLogger('replay')
+ logger.info('replay overhead {:.2%}'.format(t2 / t1 - 1))
+
+@skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout')
+class ReplayLinuxX8664(ReplayLinux):
+ """
+ :avocado: tags=arch:x86_64
+ :avocado: tags=accel:tcg
+ """
+
+ chksum = 'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0'
+
+ def test_pc_i440fx(self):
+ """
+ :avocado: tags=machine:pc
+ """
+ self.run_rr(shift=1)
+
+ def test_pc_q35(self):
+ """
+ :avocado: tags=machine:q35
+ """
+ self.run_rr(shift=3)
+
+@skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout')
+class ReplayLinuxX8664Virtio(ReplayLinux):
+ """
+ :avocado: tags=arch:x86_64
+ :avocado: tags=virtio
+ :avocado: tags=accel:tcg
+ """
+
+ hdd = 'virtio-blk-pci'
+ cd = 'virtio-blk-pci'
+ bus = None
+
+ chksum = 'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0'
+
+ def test_pc_i440fx(self):
+ """
+ :avocado: tags=machine:pc
+ """
+ self.run_rr(shift=1)
+
+ def test_pc_q35(self):
+ """
+ :avocado: tags=machine:q35
+ """
+ self.run_rr(shift=3)
+
+@skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout')
+class ReplayLinuxAarch64(ReplayLinux):
+ """
+ :avocado: tags=accel:tcg
+ :avocado: tags=arch:aarch64
+ :avocado: tags=machine:virt
+ :avocado: tags=cpu:max
+ """
+
+ chksum = '1e18d9c0cf734940c4b5d5ec592facaed2af0ad0329383d5639c997fdf16fe49'
+
+ hdd = 'virtio-blk-device'
+ cd = 'virtio-blk-device'
+ bus = None
+
+ def get_common_args(self):
+ return ('-bios',
+ os.path.join(BUILD_DIR, 'pc-bios', 'edk2-aarch64-code.fd'),
+ "-cpu", "max,lpa2=off",
+ '-device', 'virtio-rng-pci,rng=rng0',
+ '-object', 'rng-builtin,id=rng0')
+
+ def test_virt_gicv2(self):
+ """
+ :avocado: tags=machine:gic-version=2
+ """
+
+ self.run_rr(shift=3,
+ args=(*self.get_common_args(),
+ "-machine", "virt,gic-version=2"))
+
+ def test_virt_gicv3(self):
+ """
+ :avocado: tags=machine:gic-version=3
+ """
+
+ self.run_rr(shift=3,
+ args=(*self.get_common_args(),
+ "-machine", "virt,gic-version=3"))
diff --git a/tests/avocado/reverse_debugging.py b/tests/avocado/reverse_debugging.py
new file mode 100644
index 0000000000..92855a02a5
--- /dev/null
+++ b/tests/avocado/reverse_debugging.py
@@ -0,0 +1,276 @@
+# Reverse debugging test
+#
+# Copyright (c) 2020 ISP RAS
+#
+# Author:
+# Pavel Dovgalyuk <Pavel.Dovgalyuk@ispras.ru>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+import os
+import logging
+
+from avocado import skipUnless
+from avocado_qemu import BUILD_DIR
+from avocado.utils import datadrainer
+from avocado.utils import gdb
+from avocado.utils import process
+from avocado.utils.network.ports import find_free_port
+from avocado.utils.path import find_command
+from boot_linux_console import LinuxKernelTest
+
+class ReverseDebugging(LinuxKernelTest):
+ """
+ Test GDB reverse debugging commands: reverse step and reverse continue.
+ Recording saves the execution of some instructions and makes an initial
+ VM snapshot to allow reverse execution.
+ Replay saves the order of the first instructions and then checks that they
+ are executed backwards in the correct order.
+ After that the execution is replayed to the end, and reverse continue
+ command is checked by setting several breakpoints, and asserting
+ that the execution is stopped at the last of them.
+ """
+
+ timeout = 10
+ STEPS = 10
+ endian_is_le = True
+
+ def run_vm(self, record, shift, args, replay_path, image_path, port):
+ logger = logging.getLogger('replay')
+ vm = self.get_vm()
+ vm.set_console()
+ if record:
+ logger.info('recording the execution...')
+ mode = 'record'
+ else:
+ logger.info('replaying the execution...')
+ mode = 'replay'
+ vm.add_args('-gdb', 'tcp::%d' % port, '-S')
+ vm.add_args('-icount', 'shift=%s,rr=%s,rrfile=%s,rrsnapshot=init' %
+ (shift, mode, replay_path),
+ '-net', 'none')
+ vm.add_args('-drive', 'file=%s,if=none' % image_path)
+ if args:
+ vm.add_args(*args)
+ vm.launch()
+ console_drainer = datadrainer.LineLogger(vm.console_socket.fileno(),
+ logger=self.log.getChild('console'),
+ stop_check=(lambda : not vm.is_running()))
+ console_drainer.start()
+ return vm
+
+ @staticmethod
+ def get_reg_le(g, reg):
+ res = g.cmd(b'p%x' % reg)
+ num = 0
+ for i in range(len(res))[-2::-2]:
+ num = 0x100 * num + int(res[i:i + 2], 16)
+ return num
+
+ @staticmethod
+ def get_reg_be(g, reg):
+ res = g.cmd(b'p%x' % reg)
+ return int(res, 16)
+
+ def get_reg(self, g, reg):
+ # value may be encoded in BE or LE order
+ if self.endian_is_le:
+ return self.get_reg_le(g, reg)
+ else:
+ return self.get_reg_be(g, reg)
+
+ def get_pc(self, g):
+ return self.get_reg(g, self.REG_PC)
+
+ def check_pc(self, g, addr):
+ pc = self.get_pc(g)
+ if pc != addr:
+ self.fail('Invalid PC (read %x instead of %x)' % (pc, addr))
+
+ @staticmethod
+ def gdb_step(g):
+ g.cmd(b's', b'T05thread:01;')
+
+ @staticmethod
+ def gdb_bstep(g):
+ g.cmd(b'bs', b'T05thread:01;')
+
+ @staticmethod
+ def vm_get_icount(vm):
+ return vm.qmp('query-replay')['return']['icount']
+
+ def reverse_debugging(self, shift=7, args=None):
+ logger = logging.getLogger('replay')
+
+ # create qcow2 for snapshots
+ logger.info('creating qcow2 image for VM snapshots')
+ image_path = os.path.join(self.workdir, 'disk.qcow2')
+ qemu_img = os.path.join(BUILD_DIR, 'qemu-img')
+ if not os.path.exists(qemu_img):
+ qemu_img = find_command('qemu-img', False)
+ if qemu_img is False:
+ self.cancel('Could not find "qemu-img", which is required to '
+ 'create the temporary qcow2 image')
+ cmd = '%s create -f qcow2 %s 128M' % (qemu_img, image_path)
+ process.run(cmd)
+
+ replay_path = os.path.join(self.workdir, 'replay.bin')
+ port = find_free_port()
+
+ # record the log
+ vm = self.run_vm(True, shift, args, replay_path, image_path, port)
+ while self.vm_get_icount(vm) <= self.STEPS:
+ pass
+ last_icount = self.vm_get_icount(vm)
+ vm.shutdown()
+
+ logger.info("recorded log with %s+ steps" % last_icount)
+
+ # replay and run debug commands
+ vm = self.run_vm(False, shift, args, replay_path, image_path, port)
+ logger.info('connecting to gdbstub')
+ g = gdb.GDBRemote('127.0.0.1', port, False, False)
+ g.connect()
+ r = g.cmd(b'qSupported')
+ if b'qXfer:features:read+' in r:
+ g.cmd(b'qXfer:features:read:target.xml:0,ffb')
+ if b'ReverseStep+' not in r:
+ self.fail('Reverse step is not supported by QEMU')
+ if b'ReverseContinue+' not in r:
+ self.fail('Reverse continue is not supported by QEMU')
+
+ logger.info('stepping forward')
+ steps = []
+ # record first instruction addresses
+ for _ in range(self.STEPS):
+ pc = self.get_pc(g)
+ logger.info('saving position %x' % pc)
+ steps.append(pc)
+ self.gdb_step(g)
+
+ # visit the recorded instruction in reverse order
+ logger.info('stepping backward')
+ for addr in steps[::-1]:
+ self.gdb_bstep(g)
+ self.check_pc(g, addr)
+ logger.info('found position %x' % addr)
+
+ # visit the recorded instruction in forward order
+ logger.info('stepping forward')
+ for addr in steps:
+ self.check_pc(g, addr)
+ self.gdb_step(g)
+ logger.info('found position %x' % addr)
+
+ # set breakpoints for the instructions just stepped over
+ logger.info('setting breakpoints')
+ for addr in steps:
+ # hardware breakpoint at addr with len=1
+ g.cmd(b'Z1,%x,1' % addr, b'OK')
+
+ # this may hit a breakpoint if first instructions are executed
+ # again
+ logger.info('continuing execution')
+ vm.qmp('replay-break', icount=last_icount - 1)
+ # continue - will return after pausing
+ # This could stop at the end and get a T02 return, or by
+ # re-executing one of the breakpoints and get a T05 return.
+ g.cmd(b'c')
+ if self.vm_get_icount(vm) == last_icount - 1:
+ logger.info('reached the end (icount %s)' % (last_icount - 1))
+ else:
+ logger.info('hit a breakpoint again at %x (icount %s)' %
+ (self.get_pc(g), self.vm_get_icount(vm)))
+
+ logger.info('running reverse continue to reach %x' % steps[-1])
+ # reverse continue - will return after stopping at the breakpoint
+ g.cmd(b'bc', b'T05thread:01;')
+
+ # assume that none of the first instructions is executed again
+ # breaking the order of the breakpoints
+ self.check_pc(g, steps[-1])
+ logger.info('successfully reached %x' % steps[-1])
+
+ logger.info('exiting gdb and qemu')
+ vm.shutdown()
+
+class ReverseDebugging_X86_64(ReverseDebugging):
+ """
+ :avocado: tags=accel:tcg
+ """
+
+ REG_PC = 0x10
+ REG_CS = 0x12
+ def get_pc(self, g):
+ return self.get_reg_le(g, self.REG_PC) \
+ + self.get_reg_le(g, self.REG_CS) * 0x10
+
+ # unidentified gitlab timeout problem
+ @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab')
+
+ def test_x86_64_pc(self):
+ """
+ :avocado: tags=arch:x86_64
+ :avocado: tags=machine:pc
+ """
+ # start with BIOS only
+ self.reverse_debugging()
+
+class ReverseDebugging_AArch64(ReverseDebugging):
+ """
+ :avocado: tags=accel:tcg
+ """
+
+ REG_PC = 32
+
+ # unidentified gitlab timeout problem
+ @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab')
+
+ def test_aarch64_virt(self):
+ """
+ :avocado: tags=arch:aarch64
+ :avocado: tags=machine:virt
+ :avocado: tags=cpu:cortex-a53
+ """
+ kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora'
+ '/linux/releases/29/Everything/aarch64/os/images/pxeboot'
+ '/vmlinuz')
+ kernel_hash = '8c73e469fc6ea06a58dc83a628fc695b693b8493'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ self.reverse_debugging(
+ args=('-kernel', kernel_path))
+
+class ReverseDebugging_ppc64(ReverseDebugging):
+ """
+ :avocado: tags=accel:tcg
+ """
+
+ REG_PC = 0x40
+
+ # unidentified gitlab timeout problem
+ @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab')
+
+ def test_ppc64_pseries(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:pseries
+ :avocado: tags=flaky
+ """
+ # SLOF branches back to its entry point, which causes this test
+ # to take the 'hit a breakpoint again' path. That's not a problem,
+ # just slightly different than the other machines.
+ self.endian_is_le = False
+ self.reverse_debugging()
+
+ # See https://gitlab.com/qemu-project/qemu/-/issues/1992
+ @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab')
+
+ def test_ppc64_powernv(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:powernv
+ :avocado: tags=flaky
+ """
+ self.endian_is_le = False
+ self.reverse_debugging()
diff --git a/tests/avocado/riscv_opensbi.py b/tests/avocado/riscv_opensbi.py
new file mode 100644
index 0000000000..bfff9cc3c3
--- /dev/null
+++ b/tests/avocado/riscv_opensbi.py
@@ -0,0 +1,63 @@
+# OpenSBI boot test for RISC-V machines
+#
+# Copyright (c) 2022, Ventana Micro
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+
+class RiscvOpenSBI(QemuSystemTest):
+ """
+ :avocado: tags=accel:tcg
+ """
+ timeout = 5
+
+ def boot_opensbi(self):
+ self.vm.set_console()
+ self.vm.launch()
+ wait_for_console_pattern(self, 'Platform Name')
+ wait_for_console_pattern(self, 'Boot HART MEDELEG')
+
+ def test_riscv32_spike(self):
+ """
+ :avocado: tags=arch:riscv32
+ :avocado: tags=machine:spike
+ """
+ self.boot_opensbi()
+
+ def test_riscv64_spike(self):
+ """
+ :avocado: tags=arch:riscv64
+ :avocado: tags=machine:spike
+ """
+ self.boot_opensbi()
+
+ def test_riscv32_sifive_u(self):
+ """
+ :avocado: tags=arch:riscv32
+ :avocado: tags=machine:sifive_u
+ """
+ self.boot_opensbi()
+
+ def test_riscv64_sifive_u(self):
+ """
+ :avocado: tags=arch:riscv64
+ :avocado: tags=machine:sifive_u
+ """
+ self.boot_opensbi()
+
+ def test_riscv32_virt(self):
+ """
+ :avocado: tags=arch:riscv32
+ :avocado: tags=machine:virt
+ """
+ self.boot_opensbi()
+
+ def test_riscv64_virt(self):
+ """
+ :avocado: tags=arch:riscv64
+ :avocado: tags=machine:virt
+ """
+ self.boot_opensbi()
diff --git a/tests/avocado/s390_topology.py b/tests/avocado/s390_topology.py
new file mode 100644
index 0000000000..9154ac8776
--- /dev/null
+++ b/tests/avocado/s390_topology.py
@@ -0,0 +1,439 @@
+# Functional test that boots a Linux kernel and checks the console
+#
+# Copyright IBM Corp. 2023
+#
+# Author:
+# Pierre Morel <pmorel@linux.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import os
+import shutil
+import time
+
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import exec_command
+from avocado_qemu import exec_command_and_wait_for_pattern
+from avocado_qemu import interrupt_interactive_console_until_pattern
+from avocado_qemu import wait_for_console_pattern
+from avocado.utils import process
+from avocado.utils import archive
+
+
+class S390CPUTopology(QemuSystemTest):
+ """
+ S390x CPU topology consists of 4 topology layers, from bottom to top,
+ the cores, sockets, books and drawers and 2 modifiers attributes,
+ the entitlement and the dedication.
+ See: docs/system/s390x/cpu-topology.rst.
+
+ S390x CPU topology is setup in different ways:
+ - implicitly from the '-smp' argument by completing each topology
+ level one after the other beginning with drawer 0, book 0 and
+ socket 0.
+ - explicitly from the '-device' argument on the QEMU command line
+ - explicitly by hotplug of a new CPU using QMP or HMP
+ - it is modified by using QMP 'set-cpu-topology'
+
+ The S390x modifier attribute entitlement depends on the machine
+ polarization, which can be horizontal or vertical.
+ The polarization is changed on a request from the guest.
+ """
+ timeout = 90
+ event_timeout = 10
+
+ KERNEL_COMMON_COMMAND_LINE = ('printk.time=0 '
+ 'root=/dev/ram '
+ 'selinux=0 '
+ 'rdinit=/bin/sh')
+
+ def wait_until_booted(self):
+ wait_for_console_pattern(self, 'no job control',
+ failure_message='Kernel panic - not syncing',
+ vm=None)
+
+ def check_topology(self, c, s, b, d, e, t):
+ res = self.vm.qmp('query-cpus-fast')
+ cpus = res['return']
+ for cpu in cpus:
+ core = cpu['props']['core-id']
+ socket = cpu['props']['socket-id']
+ book = cpu['props']['book-id']
+ drawer = cpu['props']['drawer-id']
+ entitlement = cpu.get('entitlement')
+ dedicated = cpu.get('dedicated')
+ if core == c:
+ self.assertEqual(drawer, d)
+ self.assertEqual(book, b)
+ self.assertEqual(socket, s)
+ self.assertEqual(entitlement, e)
+ self.assertEqual(dedicated, t)
+
+ def kernel_init(self):
+ """
+ We need a VM that supports CPU topology,
+ currently this only the case when using KVM, not TCG.
+ We need a kernel supporting the CPU topology.
+ We need a minimal root filesystem with a shell.
+ """
+ self.require_accelerator("kvm")
+ kernel_url = ('https://archives.fedoraproject.org/pub/archive'
+ '/fedora-secondary/releases/35/Server/s390x/os'
+ '/images/kernel.img')
+ kernel_hash = '0d1aaaf303f07cf0160c8c48e56fe638'
+ kernel_path = self.fetch_asset(kernel_url, algorithm='md5',
+ asset_hash=kernel_hash)
+
+ initrd_url = ('https://archives.fedoraproject.org/pub/archive'
+ '/fedora-secondary/releases/35/Server/s390x/os'
+ '/images/initrd.img')
+ initrd_hash = 'a122057d95725ac030e2ec51df46e172'
+ initrd_path_xz = self.fetch_asset(initrd_url, algorithm='md5',
+ asset_hash=initrd_hash)
+ initrd_path = os.path.join(self.workdir, 'initrd-raw.img')
+ archive.lzma_uncompress(initrd_path_xz, initrd_path)
+
+ self.vm.set_console()
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE
+ self.vm.add_args('-nographic',
+ '-enable-kvm',
+ '-cpu', 'max,ctop=on',
+ '-m', '512',
+ '-kernel', kernel_path,
+ '-initrd', initrd_path,
+ '-append', kernel_command_line)
+
+ def system_init(self):
+ self.log.info("System init")
+ exec_command_and_wait_for_pattern(self,
+ """ mount proc -t proc /proc;
+ mount sys -t sysfs /sys;
+ cat /sys/devices/system/cpu/dispatching """,
+ '0')
+
+ def test_single(self):
+ """
+ This test checks the simplest topology with a single CPU.
+
+ :avocado: tags=arch:s390x
+ :avocado: tags=machine:s390-ccw-virtio
+ """
+ self.kernel_init()
+ self.vm.launch()
+ self.wait_until_booted()
+ self.check_topology(0, 0, 0, 0, 'medium', False)
+
+ def test_default(self):
+ """
+ This test checks the implicit topology.
+
+ :avocado: tags=arch:s390x
+ :avocado: tags=machine:s390-ccw-virtio
+ """
+ self.kernel_init()
+ self.vm.add_args('-smp',
+ '13,drawers=2,books=2,sockets=3,cores=2,maxcpus=24')
+ self.vm.launch()
+ self.wait_until_booted()
+ self.check_topology(0, 0, 0, 0, 'medium', False)
+ self.check_topology(1, 0, 0, 0, 'medium', False)
+ self.check_topology(2, 1, 0, 0, 'medium', False)
+ self.check_topology(3, 1, 0, 0, 'medium', False)
+ self.check_topology(4, 2, 0, 0, 'medium', False)
+ self.check_topology(5, 2, 0, 0, 'medium', False)
+ self.check_topology(6, 0, 1, 0, 'medium', False)
+ self.check_topology(7, 0, 1, 0, 'medium', False)
+ self.check_topology(8, 1, 1, 0, 'medium', False)
+ self.check_topology(9, 1, 1, 0, 'medium', False)
+ self.check_topology(10, 2, 1, 0, 'medium', False)
+ self.check_topology(11, 2, 1, 0, 'medium', False)
+ self.check_topology(12, 0, 0, 1, 'medium', False)
+
+ def test_move(self):
+ """
+ This test checks the topology modification by moving a CPU
+ to another socket: CPU 0 is moved from socket 0 to socket 2.
+
+ :avocado: tags=arch:s390x
+ :avocado: tags=machine:s390-ccw-virtio
+ """
+ self.kernel_init()
+ self.vm.add_args('-smp',
+ '1,drawers=2,books=2,sockets=3,cores=2,maxcpus=24')
+ self.vm.launch()
+ self.wait_until_booted()
+
+ self.check_topology(0, 0, 0, 0, 'medium', False)
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 0, 'socket-id': 2, 'entitlement': 'low'})
+ self.assertEqual(res['return'], {})
+ self.check_topology(0, 2, 0, 0, 'low', False)
+
+ def test_dash_device(self):
+ """
+ This test verifies that a CPU defined with the '-device'
+ command line option finds its right place inside the topology.
+
+ :avocado: tags=arch:s390x
+ :avocado: tags=machine:s390-ccw-virtio
+ """
+ self.kernel_init()
+ self.vm.add_args('-smp',
+ '1,drawers=2,books=2,sockets=3,cores=2,maxcpus=24')
+ self.vm.add_args('-device', 'max-s390x-cpu,core-id=10')
+ self.vm.add_args('-device',
+ 'max-s390x-cpu,'
+ 'core-id=1,socket-id=0,book-id=1,drawer-id=1,entitlement=low')
+ self.vm.add_args('-device',
+ 'max-s390x-cpu,'
+ 'core-id=2,socket-id=0,book-id=1,drawer-id=1,entitlement=medium')
+ self.vm.add_args('-device',
+ 'max-s390x-cpu,'
+ 'core-id=3,socket-id=1,book-id=1,drawer-id=1,entitlement=high')
+ self.vm.add_args('-device',
+ 'max-s390x-cpu,'
+ 'core-id=4,socket-id=1,book-id=1,drawer-id=1')
+ self.vm.add_args('-device',
+ 'max-s390x-cpu,'
+ 'core-id=5,socket-id=2,book-id=1,drawer-id=1,dedicated=true')
+
+ self.vm.launch()
+ self.wait_until_booted()
+
+ self.check_topology(10, 2, 1, 0, 'medium', False)
+ self.check_topology(1, 0, 1, 1, 'low', False)
+ self.check_topology(2, 0, 1, 1, 'medium', False)
+ self.check_topology(3, 1, 1, 1, 'high', False)
+ self.check_topology(4, 1, 1, 1, 'medium', False)
+ self.check_topology(5, 2, 1, 1, 'high', True)
+
+
+ def guest_set_dispatching(self, dispatching):
+ exec_command(self,
+ f'echo {dispatching} > /sys/devices/system/cpu/dispatching')
+ self.vm.event_wait('CPU_POLARIZATION_CHANGE', self.event_timeout)
+ exec_command_and_wait_for_pattern(self,
+ 'cat /sys/devices/system/cpu/dispatching', dispatching)
+
+
+ def test_polarization(self):
+ """
+ This test verifies that QEMU modifies the entitlement change after
+ several guest polarization change requests.
+
+ :avocado: tags=arch:s390x
+ :avocado: tags=machine:s390-ccw-virtio
+ """
+ self.kernel_init()
+ self.vm.launch()
+ self.wait_until_booted()
+
+ self.system_init()
+ res = self.vm.qmp('query-s390x-cpu-polarization')
+ self.assertEqual(res['return']['polarization'], 'horizontal')
+ self.check_topology(0, 0, 0, 0, 'medium', False)
+
+ self.guest_set_dispatching('1');
+ res = self.vm.qmp('query-s390x-cpu-polarization')
+ self.assertEqual(res['return']['polarization'], 'vertical')
+ self.check_topology(0, 0, 0, 0, 'medium', False)
+
+ self.guest_set_dispatching('0');
+ res = self.vm.qmp('query-s390x-cpu-polarization')
+ self.assertEqual(res['return']['polarization'], 'horizontal')
+ self.check_topology(0, 0, 0, 0, 'medium', False)
+
+
+ def check_polarization(self, polarization):
+ #We need to wait for the change to have been propagated to the kernel
+ exec_command_and_wait_for_pattern(self,
+ "\n".join([
+ "timeout 1 sh -c 'while true",
+ 'do',
+ ' syspath="/sys/devices/system/cpu/cpu0/polarization"',
+ ' polarization="$(cat "$syspath")" || exit',
+ f' if [ "$polarization" = "{polarization}" ]; then',
+ ' exit 0',
+ ' fi',
+ ' sleep 0.01',
+ #searched for strings mustn't show up in command, '' to obfuscate
+ "done' && echo succ''ess || echo fail''ure",
+ ]),
+ "success", "failure")
+
+
+ def test_entitlement(self):
+ """
+ This test verifies that QEMU modifies the entitlement
+ after a guest request and that the guest sees the change.
+
+ :avocado: tags=arch:s390x
+ :avocado: tags=machine:s390-ccw-virtio
+ """
+ self.kernel_init()
+ self.vm.launch()
+ self.wait_until_booted()
+
+ self.system_init()
+
+ self.check_polarization('horizontal')
+ self.check_topology(0, 0, 0, 0, 'medium', False)
+
+ self.guest_set_dispatching('1')
+ self.check_polarization('vertical:medium')
+ self.check_topology(0, 0, 0, 0, 'medium', False)
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 0, 'entitlement': 'low'})
+ self.assertEqual(res['return'], {})
+ self.check_polarization('vertical:low')
+ self.check_topology(0, 0, 0, 0, 'low', False)
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 0, 'entitlement': 'medium'})
+ self.assertEqual(res['return'], {})
+ self.check_polarization('vertical:medium')
+ self.check_topology(0, 0, 0, 0, 'medium', False)
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 0, 'entitlement': 'high'})
+ self.assertEqual(res['return'], {})
+ self.check_polarization('vertical:high')
+ self.check_topology(0, 0, 0, 0, 'high', False)
+
+ self.guest_set_dispatching('0');
+ self.check_polarization("horizontal")
+ self.check_topology(0, 0, 0, 0, 'high', False)
+
+
+ def test_dedicated(self):
+ """
+ This test verifies that QEMU adjusts the entitlement correctly when a
+ CPU is made dedicated.
+ QEMU retains the entitlement value when horizontal polarization is in effect.
+ For the guest, the field shows the effective value of the entitlement.
+
+ :avocado: tags=arch:s390x
+ :avocado: tags=machine:s390-ccw-virtio
+ """
+ self.kernel_init()
+ self.vm.launch()
+ self.wait_until_booted()
+
+ self.system_init()
+
+ self.check_polarization("horizontal")
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 0, 'dedicated': True})
+ self.assertEqual(res['return'], {})
+ self.check_topology(0, 0, 0, 0, 'high', True)
+ self.check_polarization("horizontal")
+
+ self.guest_set_dispatching('1');
+ self.check_topology(0, 0, 0, 0, 'high', True)
+ self.check_polarization("vertical:high")
+
+ self.guest_set_dispatching('0');
+ self.check_topology(0, 0, 0, 0, 'high', True)
+ self.check_polarization("horizontal")
+
+
+ def test_socket_full(self):
+ """
+ This test verifies that QEMU does not accept to overload a socket.
+ The socket-id 0 on book-id 0 already contains CPUs 0 and 1 and can
+ not accept any new CPU while socket-id 0 on book-id 1 is free.
+
+ :avocado: tags=arch:s390x
+ :avocado: tags=machine:s390-ccw-virtio
+ """
+ self.kernel_init()
+ self.vm.add_args('-smp',
+ '3,drawers=2,books=2,sockets=3,cores=2,maxcpus=24')
+ self.vm.launch()
+ self.wait_until_booted()
+
+ self.system_init()
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 2, 'socket-id': 0, 'book-id': 0})
+ self.assertEqual(res['error']['class'], 'GenericError')
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 2, 'socket-id': 0, 'book-id': 1})
+ self.assertEqual(res['return'], {})
+
+ def test_dedicated_error(self):
+ """
+ This test verifies that QEMU refuses to lower the entitlement
+ of a dedicated CPU
+
+ :avocado: tags=arch:s390x
+ :avocado: tags=machine:s390-ccw-virtio
+ """
+ self.kernel_init()
+ self.vm.launch()
+ self.wait_until_booted()
+
+ self.system_init()
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 0, 'dedicated': True})
+ self.assertEqual(res['return'], {})
+
+ self.check_topology(0, 0, 0, 0, 'high', True)
+
+ self.guest_set_dispatching('1');
+
+ self.check_topology(0, 0, 0, 0, 'high', True)
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 0, 'entitlement': 'low', 'dedicated': True})
+ self.assertEqual(res['error']['class'], 'GenericError')
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 0, 'entitlement': 'low'})
+ self.assertEqual(res['error']['class'], 'GenericError')
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 0, 'entitlement': 'medium', 'dedicated': True})
+ self.assertEqual(res['error']['class'], 'GenericError')
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 0, 'entitlement': 'medium'})
+ self.assertEqual(res['error']['class'], 'GenericError')
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 0, 'entitlement': 'low', 'dedicated': False})
+ self.assertEqual(res['return'], {})
+
+ res = self.vm.qmp('set-cpu-topology',
+ {'core-id': 0, 'entitlement': 'medium', 'dedicated': False})
+ self.assertEqual(res['return'], {})
+
+ def test_move_error(self):
+ """
+ This test verifies that QEMU refuses to move a CPU to an
+ nonexistent location
+
+ :avocado: tags=arch:s390x
+ :avocado: tags=machine:s390-ccw-virtio
+ """
+ self.kernel_init()
+ self.vm.launch()
+ self.wait_until_booted()
+
+ self.system_init()
+
+ res = self.vm.qmp('set-cpu-topology', {'core-id': 0, 'drawer-id': 1})
+ self.assertEqual(res['error']['class'], 'GenericError')
+
+ res = self.vm.qmp('set-cpu-topology', {'core-id': 0, 'book-id': 1})
+ self.assertEqual(res['error']['class'], 'GenericError')
+
+ res = self.vm.qmp('set-cpu-topology', {'core-id': 0, 'socket-id': 1})
+ self.assertEqual(res['error']['class'], 'GenericError')
+
+ self.check_topology(0, 0, 0, 0, 'medium', False)
diff --git a/tests/avocado/smmu.py b/tests/avocado/smmu.py
new file mode 100644
index 0000000000..21ff030ca7
--- /dev/null
+++ b/tests/avocado/smmu.py
@@ -0,0 +1,139 @@
+# SMMUv3 Functional tests
+#
+# Copyright (c) 2021 Red Hat, Inc.
+#
+# Author:
+# Eric Auger <eric.auger@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+import os
+
+from avocado import skipUnless
+from avocado_qemu import LinuxTest, BUILD_DIR
+
+@skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab')
+
+class SMMU(LinuxTest):
+ """
+ :avocado: tags=accel:kvm
+ :avocado: tags=cpu:host
+ :avocado: tags=arch:aarch64
+ :avocado: tags=machine:virt
+ :avocado: tags=distro:fedora
+ :avocado: tags=smmu
+ :avocado: tags=flaky
+ """
+
+ IOMMU_ADDON = ',iommu_platform=on,disable-modern=off,disable-legacy=on'
+ kernel_path = None
+ initrd_path = None
+ kernel_params = None
+
+ def set_up_boot(self):
+ path = self.download_boot()
+ self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,scsi=off,' +
+ 'drive=drv0,id=virtio-disk0,bootindex=1,'
+ 'werror=stop,rerror=stop' + self.IOMMU_ADDON)
+ self.vm.add_args('-drive',
+ 'file=%s,if=none,cache=writethrough,id=drv0' % path)
+
+ def setUp(self):
+ super(SMMU, self).setUp(None, 'virtio-net-pci' + self.IOMMU_ADDON)
+
+ def common_vm_setup(self, custom_kernel=False):
+ self.require_accelerator("kvm")
+ self.vm.add_args("-accel", "kvm")
+ self.vm.add_args("-cpu", "host")
+ self.vm.add_args("-machine", "iommu=smmuv3")
+ self.vm.add_args("-d", "guest_errors")
+ self.vm.add_args('-bios', os.path.join(BUILD_DIR, 'pc-bios',
+ 'edk2-aarch64-code.fd'))
+ self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0')
+ self.vm.add_args('-object',
+ 'rng-random,id=rng0,filename=/dev/urandom')
+
+ if custom_kernel is False:
+ return
+
+ kernel_url = self.distro.pxeboot_url + 'vmlinuz'
+ initrd_url = self.distro.pxeboot_url + 'initrd.img'
+ self.kernel_path = self.fetch_asset(kernel_url)
+ self.initrd_path = self.fetch_asset(initrd_url)
+
+ def run_and_check(self):
+ if self.kernel_path:
+ self.vm.add_args('-kernel', self.kernel_path,
+ '-append', self.kernel_params,
+ '-initrd', self.initrd_path)
+ self.launch_and_wait()
+ self.ssh_command('cat /proc/cmdline')
+ self.ssh_command('dnf -y install numactl-devel')
+
+
+ # 5.3 kernel without RIL #
+
+ def test_smmu_noril(self):
+ """
+ :avocado: tags=smmu_noril
+ :avocado: tags=smmu_noril_tests
+ :avocado: tags=distro_version:31
+ """
+ self.common_vm_setup()
+ self.run_and_check()
+
+ def test_smmu_noril_passthrough(self):
+ """
+ :avocado: tags=smmu_noril_passthrough
+ :avocado: tags=smmu_noril_tests
+ :avocado: tags=distro_version:31
+ """
+ self.common_vm_setup(True)
+ self.kernel_params = (self.distro.default_kernel_params +
+ ' iommu.passthrough=on')
+ self.run_and_check()
+
+ def test_smmu_noril_nostrict(self):
+ """
+ :avocado: tags=smmu_noril_nostrict
+ :avocado: tags=smmu_noril_tests
+ :avocado: tags=distro_version:31
+ """
+ self.common_vm_setup(True)
+ self.kernel_params = (self.distro.default_kernel_params +
+ ' iommu.strict=0')
+ self.run_and_check()
+
+ # 5.8 kernel featuring range invalidation
+ # >= v5.7 kernel
+
+ def test_smmu_ril(self):
+ """
+ :avocado: tags=smmu_ril
+ :avocado: tags=smmu_ril_tests
+ :avocado: tags=distro_version:33
+ """
+ self.common_vm_setup()
+ self.run_and_check()
+
+ def test_smmu_ril_passthrough(self):
+ """
+ :avocado: tags=smmu_ril_passthrough
+ :avocado: tags=smmu_ril_tests
+ :avocado: tags=distro_version:33
+ """
+ self.common_vm_setup(True)
+ self.kernel_params = (self.distro.default_kernel_params +
+ ' iommu.passthrough=on')
+ self.run_and_check()
+
+ def test_smmu_ril_nostrict(self):
+ """
+ :avocado: tags=smmu_ril_nostrict
+ :avocado: tags=smmu_ril_tests
+ :avocado: tags=distro_version:33
+ """
+ self.common_vm_setup(True)
+ self.kernel_params = (self.distro.default_kernel_params +
+ ' iommu.strict=0')
+ self.run_and_check()
diff --git a/tests/avocado/tcg_plugins.py b/tests/avocado/tcg_plugins.py
new file mode 100644
index 0000000000..15fd87b2c1
--- /dev/null
+++ b/tests/avocado/tcg_plugins.py
@@ -0,0 +1,155 @@
+# TCG Plugins tests
+#
+# These are a little more involved than the basic tests run by check-tcg.
+#
+# Copyright (c) 2021 Linaro
+#
+# Author:
+# Alex Bennée <alex.bennee@linaro.org>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import tempfile
+import mmap
+import re
+
+from boot_linux_console import LinuxKernelTest
+
+
+class PluginKernelBase(LinuxKernelTest):
+ """
+ Boots a Linux kernel with a TCG plugin enabled.
+ """
+
+ timeout = 120
+ KERNEL_COMMON_COMMAND_LINE = 'printk.time=1 panic=-1 '
+
+ def run_vm(self, kernel_path, kernel_command_line,
+ plugin, plugin_log, console_pattern, args=None):
+
+ vm = self.get_vm()
+ vm.set_console()
+ vm.add_args('-kernel', kernel_path,
+ '-append', kernel_command_line,
+ '-plugin', plugin,
+ '-d', 'plugin',
+ '-D', plugin_log,
+ '-net', 'none',
+ '-no-reboot')
+ if args:
+ vm.add_args(*args)
+
+ try:
+ vm.launch()
+ except:
+ # TODO: probably fails because plugins not enabled but we
+ # can't currently probe for the feature.
+ self.cancel("TCG Plugins not enabled?")
+
+ self.wait_for_console_pattern(console_pattern, vm)
+ # ensure logs are flushed
+ vm.shutdown()
+
+
+class PluginKernelNormal(PluginKernelBase):
+
+ def _grab_aarch64_kernel(self):
+ kernel_url = ('https://storage.tuxboot.com/20230331/arm64/Image')
+ kernel_sha256 = 'ce95a7101a5fecebe0fe630deee6bd97b32ba41bc8754090e9ad8961ea8674c7'
+ kernel_path = self.fetch_asset(kernel_url,
+ asset_hash=kernel_sha256,
+ algorithm = "sha256")
+ return kernel_path
+
+ def test_aarch64_virt_insn(self):
+ """
+ :avocado: tags=accel:tcg
+ :avocado: tags=arch:aarch64
+ :avocado: tags=machine:virt
+ :avocado: tags=cpu:cortex-a53
+ """
+ kernel_path = self._grab_aarch64_kernel()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyAMA0')
+ console_pattern = 'Kernel panic - not syncing: VFS:'
+
+ plugin_log = tempfile.NamedTemporaryFile(mode="r+t", prefix="plugin",
+ suffix=".log")
+
+ self.run_vm(kernel_path, kernel_command_line,
+ "tests/plugin/libinsn.so", plugin_log.name,
+ console_pattern)
+
+ with plugin_log as lf, \
+ mmap.mmap(lf.fileno(), 0, access=mmap.ACCESS_READ) as s:
+
+ m = re.search(br"insns: (?P<count>\d+)", s)
+ if "count" not in m.groupdict():
+ self.fail("Failed to find instruction count")
+ else:
+ count = int(m.group("count"))
+ self.log.info(f"Counted: {count} instructions")
+
+
+ def test_aarch64_virt_insn_icount(self):
+ """
+ :avocado: tags=accel:tcg
+ :avocado: tags=arch:aarch64
+ :avocado: tags=machine:virt
+ :avocado: tags=cpu:cortex-a53
+ """
+ kernel_path = self._grab_aarch64_kernel()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyAMA0')
+ console_pattern = 'Kernel panic - not syncing: VFS:'
+
+ plugin_log = tempfile.NamedTemporaryFile(mode="r+t", prefix="plugin",
+ suffix=".log")
+
+ self.run_vm(kernel_path, kernel_command_line,
+ "tests/plugin/libinsn.so", plugin_log.name,
+ console_pattern,
+ args=('-icount', 'shift=1'))
+
+ with plugin_log as lf, \
+ mmap.mmap(lf.fileno(), 0, access=mmap.ACCESS_READ) as s:
+
+ m = re.search(br"insns: (?P<count>\d+)", s)
+ if "count" not in m.groupdict():
+ self.fail("Failed to find instruction count")
+ else:
+ count = int(m.group("count"))
+ self.log.info(f"Counted: {count} instructions")
+
+ def test_aarch64_virt_mem_icount(self):
+ """
+ :avocado: tags=accel:tcg
+ :avocado: tags=arch:aarch64
+ :avocado: tags=machine:virt
+ :avocado: tags=cpu:cortex-a53
+ """
+ kernel_path = self._grab_aarch64_kernel()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyAMA0')
+ console_pattern = 'Kernel panic - not syncing: VFS:'
+
+ plugin_log = tempfile.NamedTemporaryFile(mode="r+t", prefix="plugin",
+ suffix=".log")
+
+ self.run_vm(kernel_path, kernel_command_line,
+ "tests/plugin/libmem.so,inline=true,callback=true", plugin_log.name,
+ console_pattern,
+ args=('-icount', 'shift=1'))
+
+ with plugin_log as lf, \
+ mmap.mmap(lf.fileno(), 0, access=mmap.ACCESS_READ) as s:
+ m = re.findall(br"mem accesses: (?P<count>\d+)", s)
+ if m is None or len(m) != 2:
+ self.fail("no memory access counts found")
+ else:
+ inline = int(m[0])
+ callback = int(m[1])
+ if inline != callback:
+ self.fail("mismatched access counts")
+ else:
+ self.log.info(f"Counted {inline} memory accesses")
diff --git a/tests/avocado/tesseract_utils.py b/tests/avocado/tesseract_utils.py
new file mode 100644
index 0000000000..476f528147
--- /dev/null
+++ b/tests/avocado/tesseract_utils.py
@@ -0,0 +1,46 @@
+# ...
+#
+# Copyright (c) 2019 Philippe Mathieu-Daudé <f4bug@amsat.org>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import re
+import logging
+
+from avocado.utils import process
+from avocado.utils.path import find_command, CmdNotFoundError
+
+def tesseract_available(expected_version):
+ try:
+ find_command('tesseract')
+ except CmdNotFoundError:
+ return False
+ res = process.run('tesseract --version')
+ try:
+ version = res.stdout_text.split()[1]
+ except IndexError:
+ version = res.stderr_text.split()[1]
+ return int(version.split('.')[0]) >= expected_version
+
+ match = re.match(r'tesseract\s(\d)', res)
+ if match is None:
+ return False
+ # now this is guaranteed to be a digit
+ return int(match.groups()[0]) >= expected_version
+
+
+def tesseract_ocr(image_path, tesseract_args='', tesseract_version=3):
+ console_logger = logging.getLogger('tesseract')
+ console_logger.debug(image_path)
+ if tesseract_version == 4:
+ tesseract_args += ' --oem 1'
+ proc = process.run("tesseract {} {} stdout".format(tesseract_args,
+ image_path))
+ lines = []
+ for line in proc.stdout_text.split('\n'):
+ sline = line.strip()
+ if len(sline):
+ console_logger.debug(sline)
+ lines += [sline]
+ return lines
diff --git a/tests/avocado/tuxrun_baselines.py b/tests/avocado/tuxrun_baselines.py
new file mode 100644
index 0000000000..a936a3b780
--- /dev/null
+++ b/tests/avocado/tuxrun_baselines.py
@@ -0,0 +1,620 @@
+# Functional test that boots known good tuxboot images the same way
+# that tuxrun (www.tuxrun.org) does. This tool is used by things like
+# the LKFT project to run regression tests on kernels.
+#
+# Copyright (c) 2023 Linaro Ltd.
+#
+# Author:
+# Alex Bennée <alex.bennee@linaro.org>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import os
+import time
+import tempfile
+
+from avocado import skip, skipUnless
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import exec_command, exec_command_and_wait_for_pattern
+from avocado_qemu import wait_for_console_pattern
+from avocado.utils import process
+from avocado.utils.path import find_command
+
+class TuxRunBaselineTest(QemuSystemTest):
+ """
+ :avocado: tags=accel:tcg
+ """
+
+ KERNEL_COMMON_COMMAND_LINE = 'printk.time=0'
+ # Tests are ~10-40s, allow for --debug/--enable-gcov overhead
+ timeout = 100
+
+ def get_tag(self, tagname, default=None):
+ """
+ Get the metadata tag or return the default.
+ """
+ utag = self._get_unique_tag_val(tagname)
+ print(f"{tagname}/{default} -> {utag}")
+ if utag:
+ return utag
+
+ return default
+
+ def setUp(self):
+ super().setUp()
+
+ # We need zstd for all the tuxrun tests
+ # See https://github.com/avocado-framework/avocado/issues/5609
+ zstd = find_command('zstd', False)
+ if zstd is False:
+ self.cancel('Could not find "zstd", which is required to '
+ 'decompress rootfs')
+ self.zstd = zstd
+
+ # Process the TuxRun specific tags, most machines work with
+ # reasonable defaults but we sometimes need to tweak the
+ # config. To avoid open coding everything we store all these
+ # details in the metadata for each test.
+
+ # The tuxboot tag matches the root directory
+ self.tuxboot = self.get_tag('tuxboot')
+
+ # Most Linux's use ttyS0 for their serial port
+ self.console = self.get_tag('console', "ttyS0")
+
+ # Does the machine shutdown QEMU nicely on "halt"
+ self.shutdown = self.get_tag('shutdown')
+
+ # The name of the kernel Image file
+ self.image = self.get_tag('image', "Image")
+
+ self.root = self.get_tag('root', "vda")
+
+ # Occasionally we need extra devices to hook things up
+ self.extradev = self.get_tag('extradev')
+
+ self.qemu_img = super().get_qemu_img()
+
+ def wait_for_console_pattern(self, success_message, vm=None):
+ wait_for_console_pattern(self, success_message,
+ failure_message='Kernel panic - not syncing',
+ vm=vm)
+
+ def fetch_tuxrun_assets(self, csums=None, dt=None):
+ """
+ Fetch the TuxBoot assets. They are stored in a standard way so we
+ use the per-test tags to fetch details.
+ """
+ base_url = f"https://storage.tuxboot.com/20230331/{self.tuxboot}/"
+
+ # empty hash if we weren't passed one
+ csums = {} if csums is None else csums
+ ksum = csums.get(self.image, None)
+ isum = csums.get("rootfs.ext4.zst", None)
+
+ kernel_image = self.fetch_asset(base_url + self.image,
+ asset_hash = ksum,
+ algorithm = "sha256")
+ disk_image_zst = self.fetch_asset(base_url + "rootfs.ext4.zst",
+ asset_hash = isum,
+ algorithm = "sha256")
+
+ cmd = f"{self.zstd} -d {disk_image_zst} -o {self.workdir}/rootfs.ext4"
+ process.run(cmd)
+
+ if dt:
+ dsum = csums.get(dt, None)
+ dtb = self.fetch_asset(base_url + dt,
+ asset_hash = dsum,
+ algorithm = "sha256")
+ else:
+ dtb = None
+
+ return (kernel_image, self.workdir + "/rootfs.ext4", dtb)
+
+ def prepare_run(self, kernel, disk, drive, dtb=None, console_index=0):
+ """
+ Setup to run and add the common parameters to the system
+ """
+ self.vm.set_console(console_index=console_index)
+
+ # all block devices are raw ext4's
+ blockdev = "driver=raw,file.driver=file," \
+ + f"file.filename={disk},node-name=hd0"
+
+ kcmd_line = self.KERNEL_COMMON_COMMAND_LINE
+ kcmd_line += f" root=/dev/{self.root}"
+ kcmd_line += f" console={self.console}"
+
+ self.vm.add_args('-kernel', kernel,
+ '-append', kcmd_line,
+ '-blockdev', blockdev)
+
+ # Sometimes we need extra devices attached
+ if self.extradev:
+ self.vm.add_args('-device', self.extradev)
+
+ self.vm.add_args('-device',
+ f"{drive},drive=hd0")
+
+ # Some machines need an explicit DTB
+ if dtb:
+ self.vm.add_args('-dtb', dtb)
+
+ def run_tuxtest_tests(self, haltmsg):
+ """
+ Wait for the system to boot up, wait for the login prompt and
+ then do a few things on the console. Trigger a shutdown and
+ wait to exit cleanly.
+ """
+ self.wait_for_console_pattern("Welcome to TuxTest")
+ time.sleep(0.2)
+ exec_command(self, 'root')
+ time.sleep(0.2)
+ exec_command(self, 'cat /proc/interrupts')
+ time.sleep(0.1)
+ exec_command(self, 'cat /proc/self/maps')
+ time.sleep(0.1)
+ exec_command(self, 'uname -a')
+ time.sleep(0.1)
+ exec_command_and_wait_for_pattern(self, 'halt', haltmsg)
+
+ # Wait for VM to shut down gracefully if it can
+ if self.shutdown == "nowait":
+ self.vm.shutdown()
+ else:
+ self.vm.wait()
+
+ def common_tuxrun(self,
+ csums=None,
+ dt=None,
+ drive="virtio-blk-device",
+ haltmsg="reboot: System halted",
+ console_index=0):
+ """
+ Common path for LKFT tests. Unless we need to do something
+ special with the command line we can process most things using
+ the tag metadata.
+ """
+ (kernel, disk, dtb) = self.fetch_tuxrun_assets(csums, dt)
+
+ self.prepare_run(kernel, disk, drive, dtb, console_index)
+ self.vm.launch()
+ self.run_tuxtest_tests(haltmsg)
+
+ def ppc64_common_tuxrun(self, sums, prefix):
+ # add device args to command line.
+ self.require_netdev('user')
+ self.vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22',
+ '-device', 'virtio-net,netdev=vnet')
+ self.vm.add_args('-netdev', '{"type":"user","id":"hostnet0"}',
+ '-device', '{"driver":"virtio-net-pci","netdev":'
+ '"hostnet0","id":"net0","mac":"52:54:00:4c:e3:86",'
+ '"bus":"pci.0","addr":"0x9"}')
+ self.vm.add_args('-device', '{"driver":"qemu-xhci","p2":15,"p3":15,'
+ '"id":"usb","bus":"pci.0","addr":"0x2"}')
+ self.vm.add_args('-device', '{"driver":"virtio-scsi-pci","id":"scsi0"'
+ ',"bus":"pci.0","addr":"0x3"}')
+ self.vm.add_args('-device', '{"driver":"virtio-serial-pci","id":'
+ '"virtio-serial0","bus":"pci.0","addr":"0x4"}')
+ self.vm.add_args('-device', '{"driver":"scsi-cd","bus":"scsi0.0"'
+ ',"channel":0,"scsi-id":0,"lun":0,"device_id":'
+ '"drive-scsi0-0-0-0","id":"scsi0-0-0-0"}')
+ self.vm.add_args('-device', '{"driver":"virtio-balloon-pci",'
+ '"id":"balloon0","bus":"pci.0","addr":"0x6"}')
+ self.vm.add_args('-audiodev', '{"id":"audio1","driver":"none"}')
+ self.vm.add_args('-device', '{"driver":"usb-tablet","id":"input0"'
+ ',"bus":"usb.0","port":"1"}')
+ self.vm.add_args('-device', '{"driver":"usb-kbd","id":"input1"'
+ ',"bus":"usb.0","port":"2"}')
+ self.vm.add_args('-device', '{"driver":"VGA","id":"video0",'
+ '"vgamem_mb":16,"bus":"pci.0","addr":"0x7"}')
+ self.vm.add_args('-object', '{"qom-type":"rng-random","id":"objrng0"'
+ ',"filename":"/dev/urandom"}',
+ '-device', '{"driver":"virtio-rng-pci","rng":"objrng0"'
+ ',"id":"rng0","bus":"pci.0","addr":"0x8"}')
+ self.vm.add_args('-object', '{"qom-type":"cryptodev-backend-builtin",'
+ '"id":"objcrypto0","queues":1}',
+ '-device', '{"driver":"virtio-crypto-pci",'
+ '"cryptodev":"objcrypto0","id":"crypto0","bus"'
+ ':"pci.0","addr":"0xa"}')
+ self.vm.add_args('-device', '{"driver":"spapr-pci-host-bridge"'
+ ',"index":1,"id":"pci.1"}')
+ self.vm.add_args('-device', '{"driver":"spapr-vscsi","id":"scsi1"'
+ ',"reg":12288}')
+ self.vm.add_args('-m', '2G,slots=32,maxmem=4G',
+ '-object', 'memory-backend-ram,id=ram1,size=1G',
+ '-device', 'pc-dimm,id=dimm1,memdev=ram1')
+
+ # Create a temporary qcow2 and launch the test-case
+ with tempfile.NamedTemporaryFile(prefix=prefix,
+ suffix='.qcow2') as qcow2:
+ process.run(self.qemu_img + ' create -f qcow2 ' +
+ qcow2.name + ' 1G')
+
+ self.vm.add_args('-drive', 'file=' + qcow2.name +
+ ',format=qcow2,if=none,id='
+ 'drive-virtio-disk1',
+ '-device', 'virtio-blk-pci,scsi=off,bus=pci.0,'
+ 'addr=0xb,drive=drive-virtio-disk1,id=virtio-disk1'
+ ',bootindex=2')
+ self.common_tuxrun(csums=sums, drive="scsi-hd")
+
+ #
+ # The tests themselves. The configuration is derived from how
+ # tuxrun invokes qemu (with minor tweaks like using -blockdev
+ # consistently). The tuxrun equivalent is something like:
+ #
+ # tuxrun --device qemu-{ARCH} \
+ # --kernel https://storage.tuxboot.com/{TUXBOOT}/{IMAGE}
+ #
+
+ def test_arm64(self):
+ """
+ :avocado: tags=arch:aarch64
+ :avocado: tags=cpu:cortex-a57
+ :avocado: tags=machine:virt
+ :avocado: tags=tuxboot:arm64
+ :avocado: tags=console:ttyAMA0
+ :avocado: tags=shutdown:nowait
+ """
+ sums = {"Image" :
+ "ce95a7101a5fecebe0fe630deee6bd97b32ba41bc8754090e9ad8961ea8674c7",
+ "rootfs.ext4.zst" :
+ "bbd5ed4b9c7d3f4ca19ba71a323a843c6b585e880115df3b7765769dbd9dd061"}
+ self.common_tuxrun(csums=sums)
+
+ def test_arm64be(self):
+ """
+ :avocado: tags=arch:aarch64
+ :avocado: tags=cpu:cortex-a57
+ :avocado: tags=endian:big
+ :avocado: tags=machine:virt
+ :avocado: tags=tuxboot:arm64be
+ :avocado: tags=console:ttyAMA0
+ :avocado: tags=shutdown:nowait
+ """
+ sums = { "Image" :
+ "e0df4425eb2cd9ea9a283e808037f805641c65d8fcecc8f6407d8f4f339561b4",
+ "rootfs.ext4.zst" :
+ "e6ffd8813c8a335bc15728f2835f90539c84be7f8f5f691a8b01451b47fb4bd7"}
+ self.common_tuxrun(csums=sums)
+
+ def test_armv5(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=cpu:arm926
+ :avocado: tags=machine:versatilepb
+ :avocado: tags=tuxboot:armv5
+ :avocado: tags=image:zImage
+ :avocado: tags=console:ttyAMA0
+ :avocado: tags=shutdown:nowait
+ """
+ sums = { "rootfs.ext4.zst" :
+ "17177afa74e7294da0642861f08c88ca3c836764299a54bf6d1ce276cb9712a5",
+ "versatile-pb.dtb" :
+ "0bc0c0b0858cefd3c32b385c0d66d97142ded29472a496f4f490e42fc7615b25",
+ "zImage" :
+ "c95af2f27647c12265d75e9df44c22ff5228c59855f54aaa70f41ec2842e3a4d" }
+
+ self.common_tuxrun(csums=sums,
+ drive="virtio-blk-pci",
+ dt="versatile-pb.dtb")
+
+ def test_armv7(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=cpu:cortex-a15
+ :avocado: tags=machine:virt
+ :avocado: tags=tuxboot:armv7
+ :avocado: tags=image:zImage
+ :avocado: tags=console:ttyAMA0
+ :avocado: tags=shutdown:nowait
+ """
+ sums = { "rootfs.ext4.zst" :
+ "ab1fbbeaddda1ffdd45c9405a28cd5370c20f23a7cbc809cc90dc9f243a8eb5a",
+ "zImage" :
+ "4c7a22e9f15875bec06bd2a29d822496571eb297d4f22694099ffcdb19077572" }
+
+ self.common_tuxrun(csums=sums)
+
+ def test_armv7be(self):
+ """
+ :avocado: tags=arch:arm
+ :avocado: tags=cpu:cortex-a15
+ :avocado: tags=endian:big
+ :avocado: tags=machine:virt
+ :avocado: tags=tuxboot:armv7be
+ :avocado: tags=image:zImage
+ :avocado: tags=console:ttyAMA0
+ :avocado: tags=shutdown:nowait
+ """
+ sums = {"rootfs.ext4.zst" :
+ "42ed46dd2d59986206c5b1f6cf35eab58fe3fd20c96b41aaa16b32f3f90a9835",
+ "zImage" :
+ "7facc62082b57af12015b08f7fdbaf2f123ba07a478367853ae12b219afc9f2f" }
+
+ self.common_tuxrun(csums=sums)
+
+ def test_i386(self):
+ """
+ :avocado: tags=arch:i386
+ :avocado: tags=cpu:coreduo
+ :avocado: tags=machine:q35
+ :avocado: tags=tuxboot:i386
+ :avocado: tags=image:bzImage
+ :avocado: tags=shutdown:nowait
+ """
+ sums = {"bzImage" :
+ "a3e5b32a354729e65910f5a1ffcda7c14a6c12a55e8213fb86e277f1b76ed956",
+ "rootfs.ext4.zst" :
+ "f15e66b2bf673a210ec2a4b2e744a80530b36289e04f5388aab812b97f69754a" }
+
+ self.common_tuxrun(csums=sums, drive="virtio-blk-pci")
+
+ def test_mips32(self):
+ """
+ :avocado: tags=arch:mips
+ :avocado: tags=machine:malta
+ :avocado: tags=cpu:mips32r6-generic
+ :avocado: tags=endian:big
+ :avocado: tags=tuxboot:mips32
+ :avocado: tags=image:vmlinux
+ :avocado: tags=root:sda
+ :avocado: tags=shutdown:nowait
+ """
+ sums = { "rootfs.ext4.zst" :
+ "fc3da0b4c2f38d74c6d705123bb0f633c76ed953128f9d0859378c328a6d11a0",
+ "vmlinux" :
+ "bfd2172f8b17fb32970ca0c8c58f59c5a4ca38aa5855d920be3a69b5d16e52f0" }
+
+ self.common_tuxrun(csums=sums, drive="driver=ide-hd,bus=ide.0,unit=0")
+
+ def test_mips32el(self):
+ """
+ :avocado: tags=arch:mipsel
+ :avocado: tags=machine:malta
+ :avocado: tags=cpu:mips32r6-generic
+ :avocado: tags=tuxboot:mips32el
+ :avocado: tags=image:vmlinux
+ :avocado: tags=root:sda
+ :avocado: tags=shutdown:nowait
+ """
+ sums = { "rootfs.ext4.zst" :
+ "e799768e289fd69209c21f4dacffa11baea7543d5db101e8ce27e3bc2c41d90e",
+ "vmlinux" :
+ "8573867c68a8443db8de6d08bb33fb291c189ca2ca671471d3973a3e712096a3" }
+
+ self.common_tuxrun(csums=sums, drive="driver=ide-hd,bus=ide.0,unit=0")
+
+ def test_mips64(self):
+ """
+ :avocado: tags=arch:mips64
+ :avocado: tags=machine:malta
+ :avocado: tags=tuxboot:mips64
+ :avocado: tags=endian:big
+ :avocado: tags=image:vmlinux
+ :avocado: tags=root:sda
+ :avocado: tags=shutdown:nowait
+ """
+ sums = { "rootfs.ext4.zst" :
+ "69d91eeb04df3d8d172922c6993bb37d4deeb6496def75d8580f6f9de3e431da",
+ "vmlinux" :
+ "09010e51e4b8bcbbd2494786ffb48eca78f228e96e5c5438344b0eac4029dc61" }
+
+ self.common_tuxrun(csums=sums, drive="driver=ide-hd,bus=ide.0,unit=0")
+
+ def test_mips64el(self):
+ """
+ :avocado: tags=arch:mips64el
+ :avocado: tags=machine:malta
+ :avocado: tags=tuxboot:mips64el
+ :avocado: tags=image:vmlinux
+ :avocado: tags=root:sda
+ :avocado: tags=shutdown:nowait
+ """
+ sums = { "rootfs.ext4.zst" :
+ "fba585368f5915b1498ed081863474b2d7ec4e97cdd46d21bdcb2f9698f83de4",
+ "vmlinux" :
+ "d4e08965e2155c4cccce7c5f34d18fe34c636cda2f2c9844387d614950155266" }
+
+ self.common_tuxrun(csums=sums, drive="driver=ide-hd,bus=ide.0,unit=0")
+
+ def test_ppc32(self):
+ """
+ :avocado: tags=arch:ppc
+ :avocado: tags=machine:ppce500
+ :avocado: tags=cpu:e500mc
+ :avocado: tags=tuxboot:ppc32
+ :avocado: tags=image:uImage
+ :avocado: tags=shutdown:nowait
+ """
+ sums = { "rootfs.ext4.zst" :
+ "8885b9d999cc24d679542a02e9b6aaf48f718f2050ece6b8347074b6ee41dd09",
+ "uImage" :
+ "1a68f74b860fda022fb12e03c5efece8c2b8b590d96cca37a8481a3ae0b3f81f" }
+
+ self.common_tuxrun(csums=sums, drive="virtio-blk-pci")
+
+ def test_ppc64(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:pseries
+ :avocado: tags=cpu:POWER10
+ :avocado: tags=endian:big
+ :avocado: tags=console:hvc0
+ :avocado: tags=tuxboot:ppc64
+ :avocado: tags=image:vmlinux
+ :avocado: tags=extradev:driver=spapr-vscsi
+ :avocado: tags=root:sda
+ """
+ sums = { "rootfs.ext4.zst" :
+ "1d953e81a4379e537fc8e41e05a0a59d9b453eef97aa03d47866c6c45b00bdff",
+ "vmlinux" :
+ "f22a9b9e924174a4c199f4c7e5d91a2339fcfe51c6eafd0907dc3e09b64ab728" }
+ self.ppc64_common_tuxrun(sums, prefix='tuxrun_ppc64_')
+
+ def test_ppc64le(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:pseries
+ :avocado: tags=cpu:POWER10
+ :avocado: tags=console:hvc0
+ :avocado: tags=tuxboot:ppc64le
+ :avocado: tags=image:vmlinux
+ :avocado: tags=extradev:driver=spapr-vscsi
+ :avocado: tags=root:sda
+ """
+ sums = { "rootfs.ext4.zst" :
+ "b442678c93fb8abe1f7d3bfa20556488de6b475c22c8fed363f42cf81a0a3906",
+ "vmlinux" :
+ "979eb61b445a010fb13e2b927126991f8ceef9c590fa2be0996c00e293e80cf2" }
+ self.ppc64_common_tuxrun(sums, prefix='tuxrun_ppc64le_')
+
+ def test_riscv32(self):
+ """
+ :avocado: tags=arch:riscv32
+ :avocado: tags=machine:virt
+ :avocado: tags=tuxboot:riscv32
+ """
+ sums = { "Image" :
+ "89599407d7334de629a40e7ad6503c73670359eb5f5ae9d686353a3d6deccbd5",
+ "fw_jump.elf" :
+ "f2ef28a0b77826f79d085d3e4aa686f1159b315eff9099a37046b18936676985",
+ "rootfs.ext4.zst" :
+ "7168d296d0283238ea73cd5a775b3dd608e55e04c7b92b76ecce31bb13108cba" }
+
+ self.common_tuxrun(csums=sums)
+
+ def test_riscv64(self):
+ """
+ :avocado: tags=arch:riscv64
+ :avocado: tags=machine:virt
+ :avocado: tags=tuxboot:riscv64
+ """
+ sums = { "Image" :
+ "cd634badc65e52fb63465ec99e309c0de0369f0841b7d9486f9729e119bac25e",
+ "fw_jump.elf" :
+ "6e3373abcab4305fe151b564a4c71110d833c21f2c0a1753b7935459e36aedcf",
+ "rootfs.ext4.zst" :
+ "b18e3a3bdf27be03da0b285e84cb71bf09eca071c3a087b42884b6982ed679eb" }
+
+ self.common_tuxrun(csums=sums)
+
+ def test_riscv32_maxcpu(self):
+ """
+ :avocado: tags=arch:riscv32
+ :avocado: tags=machine:virt
+ :avocado: tags=cpu:max
+ :avocado: tags=tuxboot:riscv32
+ """
+ sums = { "Image" :
+ "89599407d7334de629a40e7ad6503c73670359eb5f5ae9d686353a3d6deccbd5",
+ "fw_jump.elf" :
+ "f2ef28a0b77826f79d085d3e4aa686f1159b315eff9099a37046b18936676985",
+ "rootfs.ext4.zst" :
+ "7168d296d0283238ea73cd5a775b3dd608e55e04c7b92b76ecce31bb13108cba" }
+
+ self.common_tuxrun(csums=sums)
+
+ def test_riscv64_maxcpu(self):
+ """
+ :avocado: tags=arch:riscv64
+ :avocado: tags=machine:virt
+ :avocado: tags=cpu:max
+ :avocado: tags=tuxboot:riscv64
+ """
+ sums = { "Image" :
+ "cd634badc65e52fb63465ec99e309c0de0369f0841b7d9486f9729e119bac25e",
+ "fw_jump.elf" :
+ "6e3373abcab4305fe151b564a4c71110d833c21f2c0a1753b7935459e36aedcf",
+ "rootfs.ext4.zst" :
+ "b18e3a3bdf27be03da0b285e84cb71bf09eca071c3a087b42884b6982ed679eb" }
+
+ self.common_tuxrun(csums=sums)
+
+ def test_s390(self):
+ """
+ :avocado: tags=arch:s390x
+ :avocado: tags=endian:big
+ :avocado: tags=tuxboot:s390
+ :avocado: tags=image:bzImage
+ :avocado: tags=shutdown:nowait
+ """
+ sums = { "bzImage" :
+ "0414e98dd1c3dafff8496c9cd9c28a5f8d04553bb5ba37e906a812b48d442ef0",
+ "rootfs.ext4.zst" :
+ "88c37c32276677f873a25ab9ec6247895b8e3e6f8259134de2a616080b8ab3fc" }
+
+ self.common_tuxrun(csums=sums,
+ drive="virtio-blk-ccw",
+ haltmsg="Requesting system halt")
+
+ # Note: some segfaults caused by unaligned userspace access
+ @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab')
+ def test_sh4(self):
+ """
+ :avocado: tags=arch:sh4
+ :avocado: tags=machine:r2d
+ :avocado: tags=cpu:sh7785
+ :avocado: tags=tuxboot:sh4
+ :avocado: tags=image:zImage
+ :avocado: tags=root:sda
+ :avocado: tags=console:ttySC1
+ :avocado: tags=flaky
+ """
+ sums = { "rootfs.ext4.zst" :
+ "3592a7a3d5a641e8b9821449e77bc43c9904a56c30d45da0694349cfd86743fd",
+ "zImage" :
+ "29d9b2aba604a0f53a5dc3b5d0f2b8e35d497de1129f8ee5139eb6fdf0db692f" }
+
+ # The test is currently too unstable to do much in userspace
+ # so we skip common_tuxrun and do a minimal boot and shutdown.
+ (kernel, disk, dtb) = self.fetch_tuxrun_assets(csums=sums)
+
+ # the console comes on the second serial port
+ self.prepare_run(kernel, disk,
+ "driver=ide-hd,bus=ide.0,unit=0",
+ console_index=1)
+ self.vm.launch()
+
+ self.wait_for_console_pattern("Welcome to TuxTest")
+ time.sleep(0.1)
+ exec_command(self, 'root')
+ time.sleep(0.1)
+ exec_command_and_wait_for_pattern(self, 'halt',
+ "reboot: System halted")
+
+ def test_sparc64(self):
+ """
+ :avocado: tags=arch:sparc64
+ :avocado: tags=tuxboot:sparc64
+ :avocado: tags=image:vmlinux
+ :avocado: tags=root:sda
+ :avocado: tags=shutdown:nowait
+ """
+
+ sums = { "rootfs.ext4.zst" :
+ "ad2f1dc436ab51583543d25d2c210cab478645d47078d30d129a66ab0e281d76",
+ "vmlinux" :
+ "e34313e4325ff21deaa3d38a502aa09a373ef62b9bd4d7f8f29388b688225c55" }
+
+ self.common_tuxrun(csums=sums, drive="driver=ide-hd,bus=ide.0,unit=0")
+
+ def test_x86_64(self):
+ """
+ :avocado: tags=arch:x86_64
+ :avocado: tags=machine:q35
+ :avocado: tags=cpu:Nehalem
+ :avocado: tags=tuxboot:x86_64
+ :avocado: tags=image:bzImage
+ :avocado: tags=root:sda
+ :avocado: tags=shutdown:nowait
+ """
+ sums = { "bzImage" :
+ "2bc7480a669ee9b6b82500a236aba0c54233debe98cb968268fa230f52f03461",
+ "rootfs.ext4.zst" :
+ "b72ac729769b8f51c6dffb221113c9a063c774dbe1d66af30eb593c4e9999b4b" }
+
+ self.common_tuxrun(csums=sums,
+ drive="driver=ide-hd,bus=ide.0,unit=0")
diff --git a/tests/avocado/version.py b/tests/avocado/version.py
new file mode 100644
index 0000000000..c6139568a1
--- /dev/null
+++ b/tests/avocado/version.py
@@ -0,0 +1,25 @@
+# Version check example test
+#
+# Copyright (c) 2018 Red Hat, Inc.
+#
+# Author:
+# Cleber Rosa <crosa@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+
+from avocado_qemu import QemuSystemTest
+
+
+class Version(QemuSystemTest):
+ """
+ :avocado: tags=quick
+ :avocado: tags=machine:none
+ """
+ def test_qmp_human_info_version(self):
+ self.vm.add_args('-nodefaults')
+ self.vm.launch()
+ res = self.vm.cmd('human-monitor-command',
+ command_line='info version')
+ self.assertRegex(res, r'^(\d+\.\d+\.\d)')
diff --git a/tests/avocado/virtio-gpu.py b/tests/avocado/virtio-gpu.py
new file mode 100644
index 0000000000..6091f614a4
--- /dev/null
+++ b/tests/avocado/virtio-gpu.py
@@ -0,0 +1,157 @@
+# virtio-gpu tests
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+
+from avocado_qemu import BUILD_DIR
+from avocado_qemu import QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+from avocado_qemu import exec_command_and_wait_for_pattern
+from avocado_qemu import is_readable_executable_file
+
+from qemu.utils import kvm_available
+
+import os
+import socket
+import subprocess
+
+
+def pick_default_vug_bin():
+ relative_path = "./contrib/vhost-user-gpu/vhost-user-gpu"
+ if is_readable_executable_file(relative_path):
+ return relative_path
+
+ bld_dir_path = os.path.join(BUILD_DIR, relative_path)
+ if is_readable_executable_file(bld_dir_path):
+ return bld_dir_path
+
+
+class VirtioGPUx86(QemuSystemTest):
+ """
+ :avocado: tags=virtio-gpu
+ :avocado: tags=arch:x86_64
+ :avocado: tags=cpu:host
+ """
+
+ KERNEL_COMMAND_LINE = "printk.time=0 console=ttyS0 rdinit=/bin/bash"
+ KERNEL_URL = (
+ "https://archives.fedoraproject.org/pub/archive/fedora"
+ "/linux/releases/33/Everything/x86_64/os/images"
+ "/pxeboot/vmlinuz"
+ )
+ KERNEL_HASH = '1433cfe3f2ffaa44de4ecfb57ec25dc2399cdecf'
+ INITRD_URL = (
+ "https://archives.fedoraproject.org/pub/archive/fedora"
+ "/linux/releases/33/Everything/x86_64/os/images"
+ "/pxeboot/initrd.img"
+ )
+ INITRD_HASH = 'c828d68a027b53e5220536585efe03412332c2d9'
+
+ def wait_for_console_pattern(self, success_message, vm=None):
+ wait_for_console_pattern(
+ self,
+ success_message,
+ failure_message="Kernel panic - not syncing",
+ vm=vm,
+ )
+
+ def test_virtio_vga_virgl(self):
+ """
+ :avocado: tags=device:virtio-vga-gl
+ """
+ # FIXME: should check presence of virtio, virgl etc
+ self.require_accelerator('kvm')
+
+ kernel_path = self.fetch_asset(self.KERNEL_URL, self.KERNEL_HASH)
+ initrd_path = self.fetch_asset(self.INITRD_URL, self.INITRD_HASH)
+
+ self.vm.set_console()
+ self.vm.add_args("-m", "2G")
+ self.vm.add_args("-machine", "pc,accel=kvm")
+ self.vm.add_args("-device", "virtio-vga-gl")
+ self.vm.add_args("-display", "egl-headless")
+ self.vm.add_args(
+ "-kernel",
+ kernel_path,
+ "-initrd",
+ initrd_path,
+ "-append",
+ self.KERNEL_COMMAND_LINE,
+ )
+ try:
+ self.vm.launch()
+ except:
+ # TODO: probably fails because we are missing the VirGL features
+ self.cancel("VirGL not enabled?")
+
+ self.wait_for_console_pattern("as init process")
+ exec_command_and_wait_for_pattern(
+ self, "/usr/sbin/modprobe virtio_gpu", ""
+ )
+ self.wait_for_console_pattern("features: +virgl +edid")
+
+ def test_vhost_user_vga_virgl(self):
+ """
+ :avocado: tags=device:vhost-user-vga
+ """
+ # FIXME: should check presence of vhost-user-gpu, virgl, memfd etc
+ self.require_accelerator('kvm')
+
+ vug = pick_default_vug_bin()
+ if not vug:
+ self.cancel("Could not find vhost-user-gpu")
+
+ kernel_path = self.fetch_asset(self.KERNEL_URL, self.KERNEL_HASH)
+ initrd_path = self.fetch_asset(self.INITRD_URL, self.INITRD_HASH)
+
+ # Create socketpair to connect proxy and remote processes
+ qemu_sock, vug_sock = socket.socketpair(
+ socket.AF_UNIX, socket.SOCK_STREAM
+ )
+ os.set_inheritable(qemu_sock.fileno(), True)
+ os.set_inheritable(vug_sock.fileno(), True)
+
+ self._vug_log_path = os.path.join(
+ self.logdir, "vhost-user-gpu.log"
+ )
+ self._vug_log_file = open(self._vug_log_path, "wb")
+ self.log.info('Complete vhost-user-gpu.log file can be '
+ 'found at %s', self._vug_log_path)
+
+ vugp = subprocess.Popen(
+ [vug, "--virgl", "--fd=%d" % vug_sock.fileno()],
+ stdin=subprocess.DEVNULL,
+ stdout=self._vug_log_file,
+ stderr=subprocess.STDOUT,
+ shell=False,
+ close_fds=False,
+ )
+
+ self.vm.set_console()
+ self.vm.add_args("-m", "2G")
+ self.vm.add_args("-object", "memory-backend-memfd,id=mem,size=2G")
+ self.vm.add_args("-machine", "pc,memory-backend=mem,accel=kvm")
+ self.vm.add_args("-chardev", "socket,id=vug,fd=%d" % qemu_sock.fileno())
+ self.vm.add_args("-device", "vhost-user-vga,chardev=vug")
+ self.vm.add_args("-display", "egl-headless")
+ self.vm.add_args(
+ "-kernel",
+ kernel_path,
+ "-initrd",
+ initrd_path,
+ "-append",
+ self.KERNEL_COMMAND_LINE,
+ )
+ try:
+ self.vm.launch()
+ except:
+ # TODO: probably fails because we are missing the VirGL features
+ self.cancel("VirGL not enabled?")
+ self.wait_for_console_pattern("as init process")
+ exec_command_and_wait_for_pattern(self, "/usr/sbin/modprobe virtio_gpu",
+ "features: +virgl +edid")
+ self.vm.shutdown()
+ qemu_sock.close()
+ vugp.terminate()
+ vugp.wait()
diff --git a/tests/avocado/virtio_check_params.py b/tests/avocado/virtio_check_params.py
new file mode 100644
index 0000000000..5fe370a179
--- /dev/null
+++ b/tests/avocado/virtio_check_params.py
@@ -0,0 +1,143 @@
+#
+# Test virtio-scsi and virtio-blk queue settings for all machine types
+#
+# Copyright (c) 2019 Virtuozzo International GmbH
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import sys
+import os
+import re
+import logging
+
+from qemu.machine import QEMUMachine
+from avocado_qemu import QemuSystemTest
+from avocado import skip
+
+#list of machine types and virtqueue properties to test
+VIRTIO_SCSI_PROPS = {'seg_max_adjust': 'seg_max_adjust'}
+VIRTIO_BLK_PROPS = {'seg_max_adjust': 'seg-max-adjust'}
+
+DEV_TYPES = {'virtio-scsi-pci': VIRTIO_SCSI_PROPS,
+ 'virtio-blk-pci': VIRTIO_BLK_PROPS}
+
+VM_DEV_PARAMS = {'virtio-scsi-pci': ['-device', 'virtio-scsi-pci,id=scsi0'],
+ 'virtio-blk-pci': ['-device',
+ 'virtio-blk-pci,id=scsi0,drive=drive0',
+ '-drive',
+ 'driver=null-co,id=drive0,if=none']}
+
+
+class VirtioMaxSegSettingsCheck(QemuSystemTest):
+ @staticmethod
+ def make_pattern(props):
+ pattern_items = [r'{0} = \w+'.format(prop) for prop in props]
+ return '|'.join(pattern_items)
+
+ def query_virtqueue(self, vm, dev_type_name):
+ query_ok = False
+ error = None
+ props = None
+
+ output = vm.cmd('human-monitor-command',
+ command_line = 'info qtree')
+ props_list = DEV_TYPES[dev_type_name].values();
+ pattern = self.make_pattern(props_list)
+ res = re.findall(pattern, output)
+
+ if len(res) != len(props_list):
+ props_list = set(props_list)
+ res = set(res)
+ not_found = props_list.difference(res)
+ not_found = ', '.join(not_found)
+ error = '({0}): The following properties not found: {1}'\
+ .format(dev_type_name, not_found)
+ else:
+ query_ok = True
+ props = dict()
+ for prop in res:
+ p = prop.split(' = ')
+ props[p[0]] = p[1]
+ return query_ok, props, error
+
+ def check_mt(self, mt, dev_type_name):
+ mt['device'] = dev_type_name # Only for the debug() call.
+ logger = logging.getLogger('machine')
+ logger.debug(mt)
+ with QEMUMachine(self.qemu_bin) as vm:
+ vm.set_machine(mt["name"])
+ vm.add_args('-nodefaults')
+ for s in VM_DEV_PARAMS[dev_type_name]:
+ vm.add_args(s)
+ try:
+ vm.launch()
+ query_ok, props, error = self.query_virtqueue(vm, dev_type_name)
+ except:
+ query_ok = False
+ error = sys.exc_info()[0]
+
+ if not query_ok:
+ self.fail('machine type {0}: {1}'.format(mt['name'], error))
+
+ for prop_name, prop_val in props.items():
+ expected_val = mt[prop_name]
+ self.assertEqual(expected_val, prop_val)
+
+ @staticmethod
+ def seg_max_adjust_enabled(mt):
+ # machine types >= 5.0 should have seg_max_adjust = true
+ # others seg_max_adjust = false
+ mt = mt.split("-")
+
+ # machine types with one line name and name like pc-x.x
+ if len(mt) <= 2:
+ return False
+
+ # machine types like pc-<chip_name>-x.x[.x]
+ ver = mt[2]
+ ver = ver.split(".");
+
+ # versions >= 5.0 goes with seg_max_adjust enabled
+ major = int(ver[0])
+
+ if major >= 5:
+ return True
+ return False
+
+ @skip("break multi-arch CI")
+ def test_machine_types(self):
+ # collect all machine types except 'none', 'isapc', 'microvm'
+ with QEMUMachine(self.qemu_bin) as vm:
+ vm.launch()
+ machines = [m['name'] for m in vm.cmd('query-machines')]
+ vm.shutdown()
+ machines.remove('none')
+ machines.remove('isapc')
+ machines.remove('microvm')
+
+ for dev_type in DEV_TYPES:
+ # create the list of machine types and their parameters.
+ mtypes = list()
+ for m in machines:
+ if self.seg_max_adjust_enabled(m):
+ enabled = 'true'
+ else:
+ enabled = 'false'
+ mtypes.append({'name': m,
+ DEV_TYPES[dev_type]['seg_max_adjust']: enabled})
+
+ # test each machine type for a device type
+ for mt in mtypes:
+ self.check_mt(mt, dev_type)
diff --git a/tests/avocado/virtio_version.py b/tests/avocado/virtio_version.py
new file mode 100644
index 0000000000..afe5e828b5
--- /dev/null
+++ b/tests/avocado/virtio_version.py
@@ -0,0 +1,175 @@
+"""
+Check compatibility of virtio device types
+"""
+# Copyright (c) 2018 Red Hat, Inc.
+#
+# Author:
+# Eduardo Habkost <ehabkost@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+import sys
+import os
+
+from qemu.machine import QEMUMachine
+from avocado_qemu import QemuSystemTest
+
+# Virtio Device IDs:
+VIRTIO_NET = 1
+VIRTIO_BLOCK = 2
+VIRTIO_CONSOLE = 3
+VIRTIO_RNG = 4
+VIRTIO_BALLOON = 5
+VIRTIO_RPMSG = 7
+VIRTIO_SCSI = 8
+VIRTIO_9P = 9
+VIRTIO_RPROC_SERIAL = 11
+VIRTIO_CAIF = 12
+VIRTIO_GPU = 16
+VIRTIO_INPUT = 18
+VIRTIO_VSOCK = 19
+VIRTIO_CRYPTO = 20
+
+PCI_VENDOR_ID_REDHAT_QUMRANET = 0x1af4
+
+# Device IDs for legacy/transitional devices:
+PCI_LEGACY_DEVICE_IDS = {
+ VIRTIO_NET: 0x1000,
+ VIRTIO_BLOCK: 0x1001,
+ VIRTIO_BALLOON: 0x1002,
+ VIRTIO_CONSOLE: 0x1003,
+ VIRTIO_SCSI: 0x1004,
+ VIRTIO_RNG: 0x1005,
+ VIRTIO_9P: 0x1009,
+ VIRTIO_VSOCK: 0x1012,
+}
+
+def pci_modern_device_id(virtio_devid):
+ return virtio_devid + 0x1040
+
+def devtype_implements(vm, devtype, implements):
+ return devtype in [d['name'] for d in
+ vm.cmd('qom-list-types', implements=implements)]
+
+def get_pci_interfaces(vm, devtype):
+ interfaces = ('pci-express-device', 'conventional-pci-device')
+ return [i for i in interfaces if devtype_implements(vm, devtype, i)]
+
+class VirtioVersionCheck(QemuSystemTest):
+ """
+ Check if virtio-version-specific device types result in the
+ same device tree created by `disable-modern` and
+ `disable-legacy`.
+
+ :avocado: tags=arch:x86_64
+ """
+
+ # just in case there are failures, show larger diff:
+ maxDiff = 4096
+
+ def run_device(self, devtype, opts=None, machine='pc'):
+ """
+ Run QEMU with `-device DEVTYPE`, return device info from `query-pci`
+ """
+ with QEMUMachine(self.qemu_bin) as vm:
+ vm.set_machine(machine)
+ if opts:
+ devtype += ',' + opts
+ vm.add_args('-device', '%s,id=devfortest' % (devtype))
+ vm.add_args('-S')
+ vm.launch()
+
+ pcibuses = vm.cmd('query-pci')
+ alldevs = [dev for bus in pcibuses for dev in bus['devices']]
+ devfortest = [dev for dev in alldevs
+ if dev['qdev_id'] == 'devfortest']
+ return devfortest[0], get_pci_interfaces(vm, devtype)
+
+
+ def assert_devids(self, dev, devid, non_transitional=False):
+ self.assertEqual(dev['id']['vendor'], PCI_VENDOR_ID_REDHAT_QUMRANET)
+ self.assertEqual(dev['id']['device'], devid)
+ if non_transitional:
+ self.assertTrue(0x1040 <= dev['id']['device'] <= 0x107f)
+ self.assertGreaterEqual(dev['id']['subsystem'], 0x40)
+
+ def check_all_variants(self, qemu_devtype, virtio_devid):
+ """Check if a virtio device type and its variants behave as expected"""
+ # Force modern mode:
+ dev_modern, _ = self.run_device(qemu_devtype,
+ 'disable-modern=off,disable-legacy=on')
+ self.assert_devids(dev_modern, pci_modern_device_id(virtio_devid),
+ non_transitional=True)
+
+ # <prefix>-non-transitional device types should be 100% equivalent to
+ # <prefix>,disable-modern=off,disable-legacy=on
+ dev_1_0, nt_ifaces = self.run_device('%s-non-transitional' % (qemu_devtype))
+ self.assertEqual(dev_modern, dev_1_0)
+
+ # Force transitional mode:
+ dev_trans, _ = self.run_device(qemu_devtype,
+ 'disable-modern=off,disable-legacy=off')
+ self.assert_devids(dev_trans, PCI_LEGACY_DEVICE_IDS[virtio_devid])
+
+ # Force legacy mode:
+ dev_legacy, _ = self.run_device(qemu_devtype,
+ 'disable-modern=on,disable-legacy=off')
+ self.assert_devids(dev_legacy, PCI_LEGACY_DEVICE_IDS[virtio_devid])
+
+ # No options: default to transitional on PC machine-type:
+ no_opts_pc, generic_ifaces = self.run_device(qemu_devtype)
+ self.assertEqual(dev_trans, no_opts_pc)
+
+ #TODO: check if plugging on a PCI Express bus will make the
+ # device non-transitional
+ #no_opts_q35 = self.run_device(qemu_devtype, machine='q35')
+ #self.assertEqual(dev_modern, no_opts_q35)
+
+ # <prefix>-transitional device types should be 100% equivalent to
+ # <prefix>,disable-modern=off,disable-legacy=off
+ dev_trans, trans_ifaces = self.run_device('%s-transitional' % (qemu_devtype))
+ self.assertEqual(dev_trans, dev_trans)
+
+ # ensure the interface information is correct:
+ self.assertIn('conventional-pci-device', generic_ifaces)
+ self.assertIn('pci-express-device', generic_ifaces)
+
+ self.assertIn('conventional-pci-device', nt_ifaces)
+ self.assertIn('pci-express-device', nt_ifaces)
+
+ self.assertIn('conventional-pci-device', trans_ifaces)
+ self.assertNotIn('pci-express-device', trans_ifaces)
+
+
+ def test_conventional_devs(self):
+ self.check_all_variants('virtio-net-pci', VIRTIO_NET)
+ # virtio-blk requires 'driver' parameter
+ #self.check_all_variants('virtio-blk-pci', VIRTIO_BLOCK)
+ self.check_all_variants('virtio-serial-pci', VIRTIO_CONSOLE)
+ self.check_all_variants('virtio-rng-pci', VIRTIO_RNG)
+ self.check_all_variants('virtio-balloon-pci', VIRTIO_BALLOON)
+ self.check_all_variants('virtio-scsi-pci', VIRTIO_SCSI)
+ # virtio-9p requires 'fsdev' parameter
+ #self.check_all_variants('virtio-9p-pci', VIRTIO_9P)
+
+ def check_modern_only(self, qemu_devtype, virtio_devid):
+ """Check if a modern-only virtio device type behaves as expected"""
+ # Force modern mode:
+ dev_modern, _ = self.run_device(qemu_devtype,
+ 'disable-modern=off,disable-legacy=on')
+ self.assert_devids(dev_modern, pci_modern_device_id(virtio_devid),
+ non_transitional=True)
+
+ # No options: should be modern anyway
+ dev_no_opts, ifaces = self.run_device(qemu_devtype)
+ self.assertEqual(dev_modern, dev_no_opts)
+
+ self.assertIn('conventional-pci-device', ifaces)
+ self.assertIn('pci-express-device', ifaces)
+
+ def test_modern_only_devs(self):
+ self.check_modern_only('virtio-vga', VIRTIO_GPU)
+ self.check_modern_only('virtio-gpu-pci', VIRTIO_GPU)
+ self.check_modern_only('virtio-mouse-pci', VIRTIO_INPUT)
+ self.check_modern_only('virtio-tablet-pci', VIRTIO_INPUT)
+ self.check_modern_only('virtio-keyboard-pci', VIRTIO_INPUT)
diff --git a/tests/avocado/virtiofs_submounts.py.data/cleanup.sh b/tests/avocado/virtiofs_submounts.py.data/cleanup.sh
new file mode 100644
index 0000000000..2a6579a0fe
--- /dev/null
+++ b/tests/avocado/virtiofs_submounts.py.data/cleanup.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+function print_usage()
+{
+ if [ -n "$2" ]; then
+ echo "Error: $2"
+ echo
+ fi
+ echo "Usage: $1 <scratch dir>"
+}
+
+scratch_dir=$1
+if [ -z "$scratch_dir" ]; then
+ print_usage "$0" 'Scratch dir not given' >&2
+ exit 1
+fi
+
+cd "$scratch_dir/share" || exit 1
+mps=(mnt*)
+mp_i=0
+for mp in "${mps[@]}"; do
+ mp_i=$((mp_i + 1))
+ printf "Unmounting %i/%i...\r" "$mp_i" "${#mps[@]}"
+
+ sudo umount -R "$mp"
+ rm -rf "$mp"
+done
+echo
+
+rm some-file
+cd ..
+rmdir share
+
+imgs=(fs*.img)
+img_i=0
+for img in "${imgs[@]}"; do
+ img_i=$((img_i + 1))
+ printf "Detaching and deleting %i/%i...\r" "$img_i" "${#imgs[@]}"
+
+ dev=$(losetup -j "$img" | sed -e 's/:.*//')
+ sudo losetup -d "$dev"
+ rm -f "$img"
+done
+echo
+
+echo 'Done.'
diff --git a/tests/avocado/virtiofs_submounts.py.data/guest-cleanup.sh b/tests/avocado/virtiofs_submounts.py.data/guest-cleanup.sh
new file mode 100644
index 0000000000..729cb2d1a5
--- /dev/null
+++ b/tests/avocado/virtiofs_submounts.py.data/guest-cleanup.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+function print_usage()
+{
+ if [ -n "$2" ]; then
+ echo "Error: $2"
+ echo
+ fi
+ echo "Usage: $1 <scratch dir>"
+}
+
+scratch_dir=$1
+if [ -z "$scratch_dir" ]; then
+ print_usage "$0" 'Scratch dir not given' >&2
+ exit 1
+fi
+
+cd "$scratch_dir/share" || exit 1
+
+mps=(mnt*)
+mp_i=0
+for mp in "${mps[@]}"; do
+ mp_i=$((mp_i + 1))
+ printf "Unmounting %i/%i...\r" "$mp_i" "${#mps[@]}"
+
+ sudo umount -R "$mp"
+done
+echo
+
+echo 'Done.'
diff --git a/tests/avocado/virtiofs_submounts.py.data/guest.sh b/tests/avocado/virtiofs_submounts.py.data/guest.sh
new file mode 100644
index 0000000000..59ba40fde1
--- /dev/null
+++ b/tests/avocado/virtiofs_submounts.py.data/guest.sh
@@ -0,0 +1,138 @@
+#!/bin/bash
+
+function print_usage()
+{
+ if [ -n "$2" ]; then
+ echo "Error: $2"
+ echo
+ fi
+ echo "Usage: $1 <shared dir>"
+ echo '(The shared directory is the "share" directory in the scratch' \
+ 'directory)'
+}
+
+shared_dir=$1
+if [ -z "$shared_dir" ]; then
+ print_usage "$0" 'Shared dir not given' >&2
+ exit 1
+fi
+
+cd "$shared_dir"
+
+# FIXME: This should not be necessary, but it is. In order for all
+# submounts to be proper mount points, we need to visit them.
+# (Before we visit them, they will not be auto-mounted, and so just
+# appear as normal directories, with the catch that their st_ino will
+# be the st_ino of the filesystem they host, while the st_dev will
+# still be the st_dev of the parent.)
+# `find` does not work, because it will refuse to touch the mount
+# points as long as they are not mounted; their st_dev being shared
+# with the parent and st_ino just being the root node's inode ID
+# will practically ensure that this node exists elsewhere on the
+# filesystem, and `find` is required to recognize loops and not to
+# follow them.
+# Thus, we have to manually visit all nodes first.
+
+mnt_i=0
+
+function recursively_visit()
+{
+ pushd "$1" >/dev/null
+ for entry in *; do
+ if [[ "$entry" == mnt* ]]; then
+ mnt_i=$((mnt_i + 1))
+ printf "Triggering auto-mount $mnt_i...\r"
+ fi
+
+ if [ -d "$entry" ]; then
+ recursively_visit "$entry"
+ fi
+ done
+ popd >/dev/null
+}
+
+recursively_visit .
+echo
+
+
+if [ -n "$(find -name not-mounted)" ]; then
+ echo "Error: not-mounted files visible on mount points:" >&2
+ find -name not-mounted >&2
+ exit 1
+fi
+
+if [ ! -f some-file -o "$(cat some-file)" != 'root' ]; then
+ echo "Error: Bad file in the share root" >&2
+ exit 1
+fi
+
+shopt -s nullglob
+
+function check_submounts()
+{
+ local base_path=$1
+
+ for mp in mnt*; do
+ printf "Checking submount %i...\r" "$((${#devs[@]} + 1))"
+
+ mp_i=$(echo "$mp" | sed -e 's/mnt//')
+ dev=$(stat -c '%D' "$mp")
+
+ if [ -n "${devs[mp_i]}" ]; then
+ echo "Error: $mp encountered twice" >&2
+ exit 1
+ fi
+ devs[mp_i]=$dev
+
+ pushd "$mp" >/dev/null
+ path="$base_path$mp"
+ while true; do
+ expected_content="$(printf '%s\n%s\n' "$mp_i" "$path")"
+ if [ ! -f some-file ]; then
+ echo "Error: $PWD/some-file does not exist" >&2
+ exit 1
+ fi
+
+ if [ "$(cat some-file)" != "$expected_content" ]; then
+ echo "Error: Bad content in $PWD/some-file:" >&2
+ echo '--- found ---'
+ cat some-file
+ echo '--- expected ---'
+ echo "$expected_content"
+ exit 1
+ fi
+ if [ "$(stat -c '%D' some-file)" != "$dev" ]; then
+ echo "Error: $PWD/some-file has the wrong device ID" >&2
+ exit 1
+ fi
+
+ if [ -d sub ]; then
+ if [ "$(stat -c '%D' sub)" != "$dev" ]; then
+ echo "Error: $PWD/some-file has the wrong device ID" >&2
+ exit 1
+ fi
+ cd sub
+ path="$path/sub"
+ else
+ if [ -n "$(echo mnt*)" ]; then
+ check_submounts "$path/"
+ fi
+ break
+ fi
+ done
+ popd >/dev/null
+ done
+}
+
+root_dev=$(stat -c '%D' some-file)
+devs=()
+check_submounts ''
+echo
+
+reused_devs=$(echo "$root_dev ${devs[@]}" | tr ' ' '\n' | sort | uniq -d)
+if [ -n "$reused_devs" ]; then
+ echo "Error: Reused device IDs: $reused_devs" >&2
+ exit 1
+fi
+
+echo "Test passed for ${#devs[@]} submounts."
diff --git a/tests/avocado/virtiofs_submounts.py.data/host.sh b/tests/avocado/virtiofs_submounts.py.data/host.sh
new file mode 100644
index 0000000000..d8a9afebdb
--- /dev/null
+++ b/tests/avocado/virtiofs_submounts.py.data/host.sh
@@ -0,0 +1,127 @@
+#!/bin/bash
+
+mount_count=128
+
+function print_usage()
+{
+ if [ -n "$2" ]; then
+ echo "Error: $2"
+ echo
+ fi
+ echo "Usage: $1 <scratch dir> [seed]"
+ echo "(If no seed is given, it will be randomly generated.)"
+}
+
+scratch_dir=$1
+if [ -z "$scratch_dir" ]; then
+ print_usage "$0" 'No scratch dir given' >&2
+ exit 1
+fi
+
+if [ ! -d "$scratch_dir" ]; then
+ print_usage "$0" "$scratch_dir is not a directory" >&2
+ exit 1
+fi
+
+seed=$2
+if [ -z "$seed" ]; then
+ seed=$RANDOM
+fi
+RANDOM=$seed
+
+echo "Seed: $seed"
+
+set -e
+shopt -s nullglob
+
+cd "$scratch_dir"
+if [ -d share ]; then
+ echo 'Error: This directory seems to be in use already' >&2
+ exit 1
+fi
+
+for ((i = 0; i < $mount_count; i++)); do
+ printf "Setting up fs %i/%i...\r" "$((i + 1))" "$mount_count"
+
+ rm -f fs$i.img
+ truncate -s 512M fs$i.img
+ mkfs.xfs -q fs$i.img
+ devs[i]=$(sudo losetup -f --show fs$i.img)
+done
+echo
+
+top_level_mounts=$((RANDOM % mount_count + 1))
+
+mkdir -p share
+echo 'root' > share/some-file
+
+for ((i = 0; i < $top_level_mounts; i++)); do
+ printf "Mounting fs %i/%i...\r" "$((i + 1))" "$mount_count"
+
+ mkdir -p share/mnt$i
+ touch share/mnt$i/not-mounted
+ sudo mount "${devs[i]}" share/mnt$i
+ sudo chown "$(id -u):$(id -g)" share/mnt$i
+
+ pushd share/mnt$i >/dev/null
+ path=mnt$i
+ nesting=$((RANDOM % 4))
+ for ((j = 0; j < $nesting; j++)); do
+ cat > some-file <<EOF
+$i
+$path
+EOF
+ mkdir sub
+ cd sub
+ path="$path/sub"
+ done
+cat > some-file <<EOF
+$i
+$path
+EOF
+ popd >/dev/null
+done
+
+for ((; i < $mount_count; i++)); do
+ printf "Mounting fs %i/%i...\r" "$((i + 1))" "$mount_count"
+
+ mp_i=$((i % top_level_mounts))
+
+ pushd share/mnt$mp_i >/dev/null
+ path=mnt$mp_i
+ while true; do
+ sub_mp="$(echo mnt*)"
+ if cd sub 2>/dev/null; then
+ path="$path/sub"
+ elif [ -n "$sub_mp" ] && cd "$sub_mp" 2>/dev/null; then
+ path="$path/$sub_mp"
+ else
+ break
+ fi
+ done
+ mkdir mnt$i
+ touch mnt$i/not-mounted
+ sudo mount "${devs[i]}" mnt$i
+ sudo chown "$(id -u):$(id -g)" mnt$i
+
+ cd mnt$i
+ path="$path/mnt$i"
+ nesting=$((RANDOM % 4))
+ for ((j = 0; j < $nesting; j++)); do
+ cat > some-file <<EOF
+$i
+$path
+EOF
+ mkdir sub
+ cd sub
+ path="$path/sub"
+ done
+ cat > some-file <<EOF
+$i
+$path
+EOF
+ popd >/dev/null
+done
+echo
+
+echo 'Done.'
diff --git a/tests/avocado/vnc.py b/tests/avocado/vnc.py
new file mode 100644
index 0000000000..862c8996a8
--- /dev/null
+++ b/tests/avocado/vnc.py
@@ -0,0 +1,115 @@
+# Simple functional tests for VNC functionality
+#
+# Copyright (c) 2018 Red Hat, Inc.
+#
+# Author:
+# Cleber Rosa <crosa@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import socket
+from typing import List
+
+from avocado_qemu import QemuSystemTest
+
+
+VNC_ADDR = '127.0.0.1'
+VNC_PORT_START = 32768
+VNC_PORT_END = VNC_PORT_START + 1024
+
+
+def check_bind(port: int) -> bool:
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
+ try:
+ sock.bind((VNC_ADDR, port))
+ except OSError:
+ return False
+
+ return True
+
+
+def check_connect(port: int) -> bool:
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
+ try:
+ sock.connect((VNC_ADDR, port))
+ except ConnectionRefusedError:
+ return False
+
+ return True
+
+
+def find_free_ports(count: int) -> List[int]:
+ result = []
+ for port in range(VNC_PORT_START, VNC_PORT_END):
+ if check_bind(port):
+ result.append(port)
+ if len(result) >= count:
+ break
+ assert len(result) == count
+ return result
+
+
+class Vnc(QemuSystemTest):
+ """
+ :avocado: tags=vnc,quick
+ :avocado: tags=machine:none
+ """
+ def test_no_vnc(self):
+ self.vm.add_args('-nodefaults', '-S')
+ self.vm.launch()
+ self.assertFalse(self.vm.qmp('query-vnc')['return']['enabled'])
+
+ def test_no_vnc_change_password(self):
+ self.vm.add_args('-nodefaults', '-S')
+ self.vm.launch()
+ self.assertFalse(self.vm.qmp('query-vnc')['return']['enabled'])
+ set_password_response = self.vm.qmp('change-vnc-password',
+ password='new_password')
+ self.assertIn('error', set_password_response)
+ self.assertEqual(set_password_response['error']['class'],
+ 'GenericError')
+ self.assertEqual(set_password_response['error']['desc'],
+ 'Could not set password')
+
+ def test_change_password_requires_a_password(self):
+ self.vm.add_args('-nodefaults', '-S', '-vnc', ':0')
+ self.vm.launch()
+ self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled'])
+ set_password_response = self.vm.qmp('change-vnc-password',
+ password='new_password')
+ self.assertIn('error', set_password_response)
+ self.assertEqual(set_password_response['error']['class'],
+ 'GenericError')
+ self.assertEqual(set_password_response['error']['desc'],
+ 'Could not set password')
+
+ def test_change_password(self):
+ self.vm.add_args('-nodefaults', '-S', '-vnc', ':0,password=on')
+ self.vm.launch()
+ self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled'])
+ self.vm.cmd('change-vnc-password',
+ password='new_password')
+
+ def test_change_listen(self):
+ a, b, c = find_free_ports(3)
+ self.assertFalse(check_connect(a))
+ self.assertFalse(check_connect(b))
+ self.assertFalse(check_connect(c))
+
+ self.vm.add_args('-nodefaults', '-S', '-vnc', f'{VNC_ADDR}:{a - 5900}')
+ self.vm.launch()
+ self.assertEqual(self.vm.qmp('query-vnc')['return']['service'], str(a))
+ self.assertTrue(check_connect(a))
+ self.assertFalse(check_connect(b))
+ self.assertFalse(check_connect(c))
+
+ self.vm.cmd('display-update', type='vnc',
+ addresses=[{'type': 'inet', 'host': VNC_ADDR,
+ 'port': str(b)},
+ {'type': 'inet', 'host': VNC_ADDR,
+ 'port': str(c)}])
+ self.assertEqual(self.vm.qmp('query-vnc')['return']['service'], str(b))
+ self.assertFalse(check_connect(a))
+ self.assertTrue(check_connect(b))
+ self.assertTrue(check_connect(c))
diff --git a/tests/avocado/x86_cpu_model_versions.py b/tests/avocado/x86_cpu_model_versions.py
new file mode 100644
index 0000000000..11101e02b9
--- /dev/null
+++ b/tests/avocado/x86_cpu_model_versions.py
@@ -0,0 +1,362 @@
+#
+# Basic validation of x86 versioned CPU models and CPU model aliases
+#
+# Copyright (c) 2019 Red Hat Inc
+#
+# Author:
+# Eduardo Habkost <ehabkost@redhat.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, see <http://www.gnu.org/licenses/>.
+#
+
+
+import avocado_qemu
+import re
+
+class X86CPUModelAliases(avocado_qemu.QemuSystemTest):
+ """
+ Validation of PC CPU model versions and CPU model aliases
+
+ :avocado: tags=arch:x86_64
+ """
+ def validate_aliases(self, cpus):
+ for c in cpus.values():
+ if 'alias-of' in c:
+ # all aliases must point to a valid CPU model name:
+ self.assertIn(c['alias-of'], cpus,
+ '%s.alias-of (%s) is not a valid CPU model name' % (c['name'], c['alias-of']))
+ # aliases must not point to aliases
+ self.assertNotIn('alias-of', cpus[c['alias-of']],
+ '%s.alias-of (%s) points to another alias' % (c['name'], c['alias-of']))
+
+ # aliases must not be static
+ self.assertFalse(c['static'])
+
+ def validate_variant_aliases(self, cpus):
+ # -noTSX, -IBRS and -IBPB variants of CPU models are special:
+ # they shouldn't have their own versions:
+ self.assertNotIn("Haswell-noTSX-v1", cpus,
+ "Haswell-noTSX shouldn't be versioned")
+ self.assertNotIn("Broadwell-noTSX-v1", cpus,
+ "Broadwell-noTSX shouldn't be versioned")
+ self.assertNotIn("Nehalem-IBRS-v1", cpus,
+ "Nehalem-IBRS shouldn't be versioned")
+ self.assertNotIn("Westmere-IBRS-v1", cpus,
+ "Westmere-IBRS shouldn't be versioned")
+ self.assertNotIn("SandyBridge-IBRS-v1", cpus,
+ "SandyBridge-IBRS shouldn't be versioned")
+ self.assertNotIn("IvyBridge-IBRS-v1", cpus,
+ "IvyBridge-IBRS shouldn't be versioned")
+ self.assertNotIn("Haswell-noTSX-IBRS-v1", cpus,
+ "Haswell-noTSX-IBRS shouldn't be versioned")
+ self.assertNotIn("Haswell-IBRS-v1", cpus,
+ "Haswell-IBRS shouldn't be versioned")
+ self.assertNotIn("Broadwell-noTSX-IBRS-v1", cpus,
+ "Broadwell-noTSX-IBRS shouldn't be versioned")
+ self.assertNotIn("Broadwell-IBRS-v1", cpus,
+ "Broadwell-IBRS shouldn't be versioned")
+ self.assertNotIn("Skylake-Client-IBRS-v1", cpus,
+ "Skylake-Client-IBRS shouldn't be versioned")
+ self.assertNotIn("Skylake-Server-IBRS-v1", cpus,
+ "Skylake-Server-IBRS shouldn't be versioned")
+ self.assertNotIn("EPYC-IBPB-v1", cpus,
+ "EPYC-IBPB shouldn't be versioned")
+
+ def test_4_0_alias_compatibility(self):
+ """
+ Check if pc-*-4.0 unversioned CPU model won't be reported as aliases
+
+ :avocado: tags=machine:pc-i440fx-4.0
+ """
+ # pc-*-4.0 won't expose non-versioned CPU models as aliases
+ # We do this to help management software to keep compatibility
+ # with older QEMU versions that didn't have the versioned CPU model
+ self.vm.add_args('-S')
+ self.vm.launch()
+ cpus = dict((m['name'], m) for m in
+ self.vm.cmd('query-cpu-definitions'))
+
+ self.assertFalse(cpus['Cascadelake-Server']['static'],
+ 'unversioned Cascadelake-Server CPU model must not be static')
+ self.assertNotIn('alias-of', cpus['Cascadelake-Server'],
+ 'Cascadelake-Server must not be an alias')
+ self.assertNotIn('alias-of', cpus['Cascadelake-Server-v1'],
+ 'Cascadelake-Server-v1 must not be an alias')
+
+ self.assertFalse(cpus['qemu64']['static'],
+ 'unversioned qemu64 CPU model must not be static')
+ self.assertNotIn('alias-of', cpus['qemu64'],
+ 'qemu64 must not be an alias')
+ self.assertNotIn('alias-of', cpus['qemu64-v1'],
+ 'qemu64-v1 must not be an alias')
+
+ self.validate_variant_aliases(cpus)
+
+ # On pc-*-4.0, no CPU model should be reported as an alias:
+ for name,c in cpus.items():
+ self.assertNotIn('alias-of', c, "%s shouldn't be an alias" % (name))
+
+ def test_4_1_alias(self):
+ """
+ Check if unversioned CPU model is an alias pointing to right version
+
+ :avocado: tags=machine:pc-i440fx-4.1
+ """
+ self.vm.add_args('-S')
+ self.vm.launch()
+
+ cpus = dict((m['name'], m) for m in
+ self.vm.cmd('query-cpu-definitions'))
+
+ self.assertFalse(cpus['Cascadelake-Server']['static'],
+ 'unversioned Cascadelake-Server CPU model must not be static')
+ self.assertEqual(cpus['Cascadelake-Server'].get('alias-of'),
+ 'Cascadelake-Server-v1',
+ 'Cascadelake-Server must be an alias of Cascadelake-Server-v1')
+ self.assertNotIn('alias-of', cpus['Cascadelake-Server-v1'],
+ 'Cascadelake-Server-v1 must not be an alias')
+
+ self.assertFalse(cpus['qemu64']['static'],
+ 'unversioned qemu64 CPU model must not be static')
+ self.assertEqual(cpus['qemu64'].get('alias-of'), 'qemu64-v1',
+ 'qemu64 must be an alias of qemu64-v1')
+ self.assertNotIn('alias-of', cpus['qemu64-v1'],
+ 'qemu64-v1 must not be an alias')
+
+ self.validate_variant_aliases(cpus)
+
+ # On pc-*-4.1, -noTSX and -IBRS models should be aliases:
+ self.assertEqual(cpus["Haswell"].get('alias-of'),
+ "Haswell-v1",
+ "Haswell must be an alias")
+ self.assertEqual(cpus["Haswell-noTSX"].get('alias-of'),
+ "Haswell-v2",
+ "Haswell-noTSX must be an alias")
+ self.assertEqual(cpus["Haswell-IBRS"].get('alias-of'),
+ "Haswell-v3",
+ "Haswell-IBRS must be an alias")
+ self.assertEqual(cpus["Haswell-noTSX-IBRS"].get('alias-of'),
+ "Haswell-v4",
+ "Haswell-noTSX-IBRS must be an alias")
+
+ self.assertEqual(cpus["Broadwell"].get('alias-of'),
+ "Broadwell-v1",
+ "Broadwell must be an alias")
+ self.assertEqual(cpus["Broadwell-noTSX"].get('alias-of'),
+ "Broadwell-v2",
+ "Broadwell-noTSX must be an alias")
+ self.assertEqual(cpus["Broadwell-IBRS"].get('alias-of'),
+ "Broadwell-v3",
+ "Broadwell-IBRS must be an alias")
+ self.assertEqual(cpus["Broadwell-noTSX-IBRS"].get('alias-of'),
+ "Broadwell-v4",
+ "Broadwell-noTSX-IBRS must be an alias")
+
+ self.assertEqual(cpus["Nehalem"].get('alias-of'),
+ "Nehalem-v1",
+ "Nehalem must be an alias")
+ self.assertEqual(cpus["Nehalem-IBRS"].get('alias-of'),
+ "Nehalem-v2",
+ "Nehalem-IBRS must be an alias")
+
+ self.assertEqual(cpus["Westmere"].get('alias-of'),
+ "Westmere-v1",
+ "Westmere must be an alias")
+ self.assertEqual(cpus["Westmere-IBRS"].get('alias-of'),
+ "Westmere-v2",
+ "Westmere-IBRS must be an alias")
+
+ self.assertEqual(cpus["SandyBridge"].get('alias-of'),
+ "SandyBridge-v1",
+ "SandyBridge must be an alias")
+ self.assertEqual(cpus["SandyBridge-IBRS"].get('alias-of'),
+ "SandyBridge-v2",
+ "SandyBridge-IBRS must be an alias")
+
+ self.assertEqual(cpus["IvyBridge"].get('alias-of'),
+ "IvyBridge-v1",
+ "IvyBridge must be an alias")
+ self.assertEqual(cpus["IvyBridge-IBRS"].get('alias-of'),
+ "IvyBridge-v2",
+ "IvyBridge-IBRS must be an alias")
+
+ self.assertEqual(cpus["Skylake-Client"].get('alias-of'),
+ "Skylake-Client-v1",
+ "Skylake-Client must be an alias")
+ self.assertEqual(cpus["Skylake-Client-IBRS"].get('alias-of'),
+ "Skylake-Client-v2",
+ "Skylake-Client-IBRS must be an alias")
+
+ self.assertEqual(cpus["Skylake-Server"].get('alias-of'),
+ "Skylake-Server-v1",
+ "Skylake-Server must be an alias")
+ self.assertEqual(cpus["Skylake-Server-IBRS"].get('alias-of'),
+ "Skylake-Server-v2",
+ "Skylake-Server-IBRS must be an alias")
+
+ self.assertEqual(cpus["EPYC"].get('alias-of'),
+ "EPYC-v1",
+ "EPYC must be an alias")
+ self.assertEqual(cpus["EPYC-IBPB"].get('alias-of'),
+ "EPYC-v2",
+ "EPYC-IBPB must be an alias")
+
+ self.validate_aliases(cpus)
+
+ def test_none_alias(self):
+ """
+ Check if unversioned CPU model is an alias pointing to some version
+
+ :avocado: tags=machine:none
+ """
+ self.vm.add_args('-S')
+ self.vm.launch()
+
+ cpus = dict((m['name'], m) for m in
+ self.vm.cmd('query-cpu-definitions'))
+
+ self.assertFalse(cpus['Cascadelake-Server']['static'],
+ 'unversioned Cascadelake-Server CPU model must not be static')
+ self.assertTrue(re.match('Cascadelake-Server-v[0-9]+', cpus['Cascadelake-Server']['alias-of']),
+ 'Cascadelake-Server must be an alias of versioned CPU model')
+ self.assertNotIn('alias-of', cpus['Cascadelake-Server-v1'],
+ 'Cascadelake-Server-v1 must not be an alias')
+
+ self.assertFalse(cpus['qemu64']['static'],
+ 'unversioned qemu64 CPU model must not be static')
+ self.assertTrue(re.match('qemu64-v[0-9]+', cpus['qemu64']['alias-of']),
+ 'qemu64 must be an alias of versioned CPU model')
+ self.assertNotIn('alias-of', cpus['qemu64-v1'],
+ 'qemu64-v1 must not be an alias')
+
+ self.validate_aliases(cpus)
+
+
+class CascadelakeArchCapabilities(avocado_qemu.QemuSystemTest):
+ """
+ Validation of Cascadelake arch-capabilities
+
+ :avocado: tags=arch:x86_64
+ """
+ def get_cpu_prop(self, prop):
+ cpu_path = self.vm.cmd('query-cpus-fast')[0].get('qom-path')
+ return self.vm.cmd('qom-get', path=cpu_path, property=prop)
+
+ def test_4_1(self):
+ """
+ :avocado: tags=machine:pc-i440fx-4.1
+ :avocado: tags=cpu:Cascadelake-Server
+ """
+ # machine-type only:
+ self.vm.add_args('-S')
+ self.set_vm_arg('-cpu',
+ 'Cascadelake-Server,x-force-features=on,check=off,'
+ 'enforce=off')
+ self.vm.launch()
+ self.assertFalse(self.get_cpu_prop('arch-capabilities'),
+ 'pc-i440fx-4.1 + Cascadelake-Server should not have arch-capabilities')
+
+ def test_4_0(self):
+ """
+ :avocado: tags=machine:pc-i440fx-4.0
+ :avocado: tags=cpu:Cascadelake-Server
+ """
+ self.vm.add_args('-S')
+ self.set_vm_arg('-cpu',
+ 'Cascadelake-Server,x-force-features=on,check=off,'
+ 'enforce=off')
+ self.vm.launch()
+ self.assertFalse(self.get_cpu_prop('arch-capabilities'),
+ 'pc-i440fx-4.0 + Cascadelake-Server should not have arch-capabilities')
+
+ def test_set_4_0(self):
+ """
+ :avocado: tags=machine:pc-i440fx-4.0
+ :avocado: tags=cpu:Cascadelake-Server
+ """
+ # command line must override machine-type if CPU model is not versioned:
+ self.vm.add_args('-S')
+ self.set_vm_arg('-cpu',
+ 'Cascadelake-Server,x-force-features=on,check=off,'
+ 'enforce=off,+arch-capabilities')
+ self.vm.launch()
+ self.assertTrue(self.get_cpu_prop('arch-capabilities'),
+ 'pc-i440fx-4.0 + Cascadelake-Server,+arch-capabilities should have arch-capabilities')
+
+ def test_unset_4_1(self):
+ """
+ :avocado: tags=machine:pc-i440fx-4.1
+ :avocado: tags=cpu:Cascadelake-Server
+ """
+ self.vm.add_args('-S')
+ self.set_vm_arg('-cpu',
+ 'Cascadelake-Server,x-force-features=on,check=off,'
+ 'enforce=off,-arch-capabilities')
+ self.vm.launch()
+ self.assertFalse(self.get_cpu_prop('arch-capabilities'),
+ 'pc-i440fx-4.1 + Cascadelake-Server,-arch-capabilities should not have arch-capabilities')
+
+ def test_v1_4_0(self):
+ """
+ :avocado: tags=machine:pc-i440fx-4.0
+ :avocado: tags=cpu:Cascadelake-Server
+ """
+ # versioned CPU model overrides machine-type:
+ self.vm.add_args('-S')
+ self.set_vm_arg('-cpu',
+ 'Cascadelake-Server-v1,x-force-features=on,check=off,'
+ 'enforce=off')
+ self.vm.launch()
+ self.assertFalse(self.get_cpu_prop('arch-capabilities'),
+ 'pc-i440fx-4.0 + Cascadelake-Server-v1 should not have arch-capabilities')
+
+ def test_v2_4_0(self):
+ """
+ :avocado: tags=machine:pc-i440fx-4.0
+ :avocado: tags=cpu:Cascadelake-Server
+ """
+ self.vm.add_args('-S')
+ self.set_vm_arg('-cpu',
+ 'Cascadelake-Server-v2,x-force-features=on,check=off,'
+ 'enforce=off')
+ self.vm.launch()
+ self.assertTrue(self.get_cpu_prop('arch-capabilities'),
+ 'pc-i440fx-4.0 + Cascadelake-Server-v2 should have arch-capabilities')
+
+ def test_v1_set_4_0(self):
+ """
+ :avocado: tags=machine:pc-i440fx-4.0
+ :avocado: tags=cpu:Cascadelake-Server
+ """
+ # command line must override machine-type and versioned CPU model:
+ self.vm.add_args('-S')
+ self.set_vm_arg('-cpu',
+ 'Cascadelake-Server-v1,x-force-features=on,check=off,'
+ 'enforce=off,+arch-capabilities')
+ self.vm.launch()
+ self.assertTrue(self.get_cpu_prop('arch-capabilities'),
+ 'pc-i440fx-4.0 + Cascadelake-Server-v1,+arch-capabilities should have arch-capabilities')
+
+ def test_v2_unset_4_1(self):
+ """
+ :avocado: tags=machine:pc-i440fx-4.1
+ :avocado: tags=cpu:Cascadelake-Server
+ """
+ self.vm.add_args('-S')
+ self.set_vm_arg('-cpu',
+ 'Cascadelake-Server-v2,x-force-features=on,check=off,'
+ 'enforce=off,-arch-capabilities')
+ self.vm.launch()
+ self.assertFalse(self.get_cpu_prop('arch-capabilities'),
+ 'pc-i440fx-4.1 + Cascadelake-Server-v2,-arch-capabilities should not have arch-capabilities')