Currently, kunit.py will default to 'um' (User-Mode Linux) if no specific --arch option is given. However, UML is only available on x86-based architectures, so kunit.py fails by default on anything else.
Instead, for non-x86 architectures, enumerate the qemu configs looking for one which matches the current architecture. This currently uses `uname -m`, which doesn't always match the kernel/kunit's architecture name (e.g., aarch64 versus arm64), so look at both the kernel and qemu architecture names in the qemu config until one matches.
With this change `./tools/testing/kunit/kunit.py run` should function out-of-the-box on most non-x86 architectures, assuming qemu is installed.
Signed-off-by: David Gow davidgow@google.com --- tools/testing/kunit/kunit.py | 2 +- tools/testing/kunit/kunit_kernel.py | 43 ++++++++++++++++++++------ tools/testing/kunit/kunit_tool_test.py | 10 +++--- 3 files changed, 40 insertions(+), 15 deletions(-)
diff --git a/tools/testing/kunit/kunit.py b/tools/testing/kunit/kunit.py index 7f9ae55fd6d5..188bb7f2802f 100755 --- a/tools/testing/kunit/kunit.py +++ b/tools/testing/kunit/kunit.py @@ -351,7 +351,7 @@ def add_common_opts(parser: argparse.ArgumentParser) -> None: 'string passed to the ARCH make param, ' 'e.g. i386, x86_64, arm, um, etc. Non-UML ' 'architectures run on QEMU.'), - type=str, default='um', metavar='ARCH') + type=str, metavar='ARCH')
parser.add_argument('--cross_compile', help=('Sets make's CROSS_COMPILE variable; it should ' diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py index c3201a76da24..7042e44e3a88 100644 --- a/tools/testing/kunit/kunit_kernel.py +++ b/tools/testing/kunit/kunit_kernel.py @@ -211,6 +211,31 @@ def _default_qemu_config_path(arch: str) -> str:
raise ConfigError(arch + ' is not a valid arch, options are ' + str(sorted(options)))
+def _detect_default_architecture() -> str: + uname_arch = os.uname().machine + + options = [f[:-3] for f in os.listdir(QEMU_CONFIGS_DIR) if f.endswith('.py')] + + if uname_arch == 'x86_64' or uname_arch == 'i486' or uname_arch == 'i586' or uname_arch == 'i686': + return 'um' + + for option in options: + config_path = os.path.join(QEMU_CONFIGS_DIR, option + '.py') + module_path = '.' + os.path.join(os.path.basename(QEMU_CONFIGS_DIR), os.path.basename(config_path)) + spec = importlib.util.spec_from_file_location(module_path, config_path) + assert spec is not None + config = importlib.util.module_from_spec(spec) + # See https://github.com/python/typeshed/pull/2626 for context. + assert isinstance(spec.loader, importlib.abc.Loader) + spec.loader.exec_module(config) + + if config.QEMU_ARCH.linux_arch == uname_arch: + return option + if config.QEMU_ARCH.qemu_arch == uname_arch: + return option + + raise ConfigError('Could not find a valid config for ' + uname_arch + ', options are ' + str(sorted(options))) + def _get_qemu_ops(config_path: str, extra_qemu_args: Optional[List[str]], cross_compile: Optional[str]) -> Tuple[str, LinuxSourceTreeOperations]: @@ -247,19 +272,19 @@ class LinuxSourceTree: """Represents a Linux kernel source tree with KUnit tests."""
def __init__( - self, - build_dir: str, - kunitconfig_paths: Optional[List[str]]=None, - kconfig_add: Optional[List[str]]=None, - arch: Optional[str]=None, - cross_compile: Optional[str]=None, - qemu_config_path: Optional[str]=None, - extra_qemu_args: Optional[List[str]]=None) -> None: + self, + build_dir: str, + kunitconfig_paths: Optional[List[str]]=None, + kconfig_add: Optional[List[str]]=None, + arch: Optional[str]=None, + cross_compile: Optional[str]=None, + qemu_config_path: Optional[str]=None, + extra_qemu_args: Optional[List[str]]=None) -> None: signal.signal(signal.SIGINT, self.signal_handler) if qemu_config_path: self._arch, self._ops = _get_qemu_ops(qemu_config_path, extra_qemu_args, cross_compile) else: - self._arch = 'um' if arch is None else arch + self._arch = _detect_default_architecture() if arch is None else arch if self._arch == 'um': self._ops = LinuxSourceTreeOperationsUml(cross_compile=cross_compile) else: diff --git a/tools/testing/kunit/kunit_tool_test.py b/tools/testing/kunit/kunit_tool_test.py index bbba921e0eac..a3dd456d62cd 100755 --- a/tools/testing/kunit/kunit_tool_test.py +++ b/tools/testing/kunit/kunit_tool_test.py @@ -747,7 +747,7 @@ class KUnitMainTest(unittest.TestCase): self.mock_linux_init.assert_called_once_with('.kunit', kunitconfig_paths=['mykunitconfig'], kconfig_add=None, - arch='um', + arch=None, cross_compile=None, qemu_config_path=None, extra_qemu_args=[]) @@ -758,7 +758,7 @@ class KUnitMainTest(unittest.TestCase): self.mock_linux_init.assert_called_once_with('.kunit', kunitconfig_paths=['mykunitconfig'], kconfig_add=None, - arch='um', + arch=None, cross_compile=None, qemu_config_path=None, extra_qemu_args=[]) @@ -769,7 +769,7 @@ class KUnitMainTest(unittest.TestCase): self.mock_linux_init.assert_called_once_with('.kunit', kunitconfig_paths=[kunit_kernel.ALL_TESTS_CONFIG_PATH, 'mykunitconfig'], kconfig_add=None, - arch='um', + arch=None, cross_compile=None, qemu_config_path=None, extra_qemu_args=[]) @@ -783,7 +783,7 @@ class KUnitMainTest(unittest.TestCase): mock_linux_init.assert_called_once_with('.kunit', kunitconfig_paths=['mykunitconfig', 'other'], kconfig_add=None, - arch='um', + arch=None, cross_compile=None, qemu_config_path=None, extra_qemu_args=[]) @@ -794,7 +794,7 @@ class KUnitMainTest(unittest.TestCase): self.mock_linux_init.assert_called_once_with('.kunit', kunitconfig_paths=[], kconfig_add=['CONFIG_KASAN=y', 'CONFIG_KCSAN=y'], - arch='um', + arch=None, cross_compile=None, qemu_config_path=None, extra_qemu_args=[])