diff --git a/Runner/suites/Virtualization/KVM/KVM_Driver/KVM_Driver.yaml b/Runner/suites/Virtualization/KVM/KVM_Driver/KVM_Driver.yaml new file mode 100755 index 00000000..f68f0188 --- /dev/null +++ b/Runner/suites/Virtualization/KVM/KVM_Driver/KVM_Driver.yaml @@ -0,0 +1,17 @@ +metadata: + name: KVM_Driver + format: "Lava-Test Test Definition 1.0" + description: "Validate /dev/kvm device node and KVM ioctl API" + maintainer: + - Srikanth kumar + os: + - linux + scope: + - functional + +run: + steps: + - REPO_PATH=$PWD + - cd Runner/suites/Virtualization/KVM/KVM_Driver + - ./run.sh || true + - $REPO_PATH/Runner/utils/send-to-lava.sh KVM_Driver.res diff --git a/Runner/suites/Virtualization/KVM/KVM_Driver/README.md b/Runner/suites/Virtualization/KVM/KVM_Driver/README.md new file mode 100644 index 00000000..0fd7f320 --- /dev/null +++ b/Runner/suites/Virtualization/KVM/KVM_Driver/README.md @@ -0,0 +1,183 @@ +# KVM_Driver + +## Overview + +`KVM_Driver` validates the mandatory KVM host configuration, `/dev/kvm` +runtime device node, and KVM userspace ioctl API. + +This test now covers the baseline KVM boot/config validation that was previously +covered separately by `KVM_Boot_Up`, and then performs a stronger functional +driver check by opening `/dev/kvm`, validating the KVM API version, and +attempting a safe `KVM_CREATE_VM` ioctl through the shared `lib_kvm.sh` helper. + +This test does not launch QEMU and does not boot a guest VM. + +## Test location + +```text +Runner/suites/Virtualization/KVM/KVM_Driver/ +``` + +## Files + +```text +run.sh +KVM_Driver.yaml +README.md +``` + +## Dependencies + +The test uses common helpers from: + +```text +Runner/utils/functestlib.sh +Runner/utils/lib_kvm.sh +``` + +Required target utilities: + +```text +cat grep awk sed tr mkdir uname +``` + +Additional helper dependency: + +```text +python3 +``` + +`python3` is used by `kvm_check_api_version()` to issue the `/dev/kvm` ioctl +checks without requiring a prebuilt C helper binary. If `python3` is not +present, the test reports `SKIP`. + +## Validation coverage + +The test validates: + +1. Mandatory kernel config support: + - `CONFIG_VIRTUALIZATION` + - `CONFIG_KVM` + +2. Optional KVM-related configs are logged when visible: + - `CONFIG_HAVE_KVM` + - `CONFIG_HAVE_KVM_IRQCHIP` + - `CONFIG_HAVE_KVM_IRQFD` + - `CONFIG_KVM_ARM_PMU` + - `CONFIG_KVM_GENERIC_DIRTYLOG_READ_PROTECT` + +3. Runtime device node: + - `/dev/kvm` exists + - `/dev/kvm` is a character device + - `/dev/kvm` is readable and writable + +4. KVM API ioctl path: + - `open("/dev/kvm")` + - `KVM_GET_API_VERSION` + - API version must be `12` + - `KVM_CREATE_VM` + +5. Kernel log scan: + - checks for fatal KVM/EL2/HYP/GIC related runtime errors + +## Result policy + +### PASS + +The test reports `PASS` when: + +- mandatory KVM configs are enabled, +- `/dev/kvm` is present and accessible, +- KVM ioctl API validation passes, +- no fatal KVM/EL2 errors are detected in kernel logs. + +### SKIP + +The test reports `SKIP` when: + +- `python3` is not available for the ioctl helper, +- required userspace utilities are missing, +- the testcase path or setup environment cannot be resolved before test + execution starts. + +### FAIL + +The test reports `FAIL` when: + +- `CONFIG_VIRTUALIZATION` is not enabled, +- `CONFIG_KVM` is not enabled, +- `/dev/kvm` is not present, +- `/dev/kvm` exists but is not usable, +- KVM ioctl API validation fails, +- fatal KVM/EL2/HYP related errors are detected in kernel logs. + +## Manual execution + +From the repository root on target: + +```sh +cd Runner/suites/Virtualization/KVM/KVM_Driver +./run.sh +cat KVM_Driver.res +``` + +Expected result file: + +```text +KVM_Driver PASS +``` + +or: + +```text +KVM_Driver SKIP +``` + +or: + +```text +KVM_Driver FAIL +``` + +## LAVA execution + +The YAML file runs: + +```sh +cd Runner/suites/Virtualization/KVM/KVM_Driver +./run.sh || true +$REPO_PATH/Runner/utils/send-to-lava.sh KVM_Driver.res +``` + +## Logs + +The test creates logs under: + +```text +results/KVM_Driver/ +``` + +KVM/EL2 dmesg logs are captured under: + +```text +results/KVM_Driver/dmesg/ +``` + +## Notes + +`KVM_Driver` intentionally includes the baseline KVM boot/config checks so a +separate `KVM_Boot_Up` test is not required. + +This test does not launch QEMU or boot a guest VM. That coverage belongs to: + +```text +KVM_Infra +QEMU_VM_Validation +``` + +This test also does not validate EL2-DTB remoteproc/IOMMU evidence. That is +covered by: + +```text +KVM_EL2_DTB +``` diff --git a/Runner/suites/Virtualization/KVM/KVM_Driver/run.sh b/Runner/suites/Virtualization/KVM/KVM_Driver/run.sh new file mode 100755 index 00000000..74bf8260 --- /dev/null +++ b/Runner/suites/Virtualization/KVM/KVM_Driver/run.sh @@ -0,0 +1,152 @@ +#!/bin/sh +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause + +TESTNAME="KVM_Driver" + +# Robustly find and source init_env +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +INIT_ENV="" +SEARCH="$SCRIPT_DIR" + +while [ "$SEARCH" != "/" ]; do + if [ -f "$SEARCH/init_env" ]; then + INIT_ENV="$SEARCH/init_env" + break + fi + SEARCH=$(dirname "$SEARCH") +done + +RES_FALLBACK="$SCRIPT_DIR/${TESTNAME}.res" + +if [ -z "$INIT_ENV" ]; then + echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + echo "$TESTNAME SKIP" >"$RES_FALLBACK" 2>/dev/null || true + exit 0 +fi + +# Only source if not already loaded (idempotent) +if [ -z "${__INIT_ENV_LOADED:-}" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" + __INIT_ENV_LOADED=1 +fi + +# Always source functestlib.sh, using $TOOLS exported by init_env +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/functestlib.sh" + +# Source KVM helper library +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/lib_kvm.sh" + +test_path=$(find_test_case_by_name "$TESTNAME") +if [ -z "$test_path" ] || [ ! -d "$test_path" ]; then + log_skip "$TESTNAME SKIP - test path not found" + echo "$TESTNAME SKIP" >"$RES_FALLBACK" 2>/dev/null || true + exit 0 +fi + +if ! cd "$test_path"; then + log_skip "$TESTNAME SKIP - cannot cd into $test_path" + echo "$TESTNAME SKIP" >"$RES_FALLBACK" 2>/dev/null || true + exit 0 +fi + +# shellcheck disable=SC2034 +res_file="./$TESTNAME.res" +RESULT_DIR="./results/$TESTNAME" +DMESG_DIR="$RESULT_DIR/dmesg" + +mkdir -p "$DMESG_DIR" 2>/dev/null || true + +log_info "-----------------------------------------------------------------------------------------" +log_info "-------------------Starting $TESTNAME Testcase----------------------------" +log_info "=== Test Initialization ===" + +deps_list="cat grep awk sed tr mkdir uname" +log_info "Checking dependencies: $deps_list" +if ! check_dependencies "$deps_list"; then + log_skip "$TESTNAME SKIP - missing one or more dependencies: $deps_list" + echo "$TESTNAME SKIP" >"$res_file" + exit 0 +fi + +if command -v detect_platform >/dev/null 2>&1; then + detect_platform >/dev/null 2>&1 || true + log_info "Platform Details: machine='${PLATFORM_MACHINE:-unknown}' target='${PLATFORM_TARGET:-unknown}' kernel='${PLATFORM_KERNEL:-}' arch='${PLATFORM_ARCH:-}'" +else + log_info "Platform Details: kernel='$(uname -r 2>/dev/null || echo unknown)' arch='$(uname -m 2>/dev/null || echo unknown)'" +fi + +log_info "=== KVM Kernel Config Validation ===" + +mandatory_cfg_missing=0 + +for cfg in CONFIG_VIRTUALIZATION CONFIG_KVM; do + if ! check_kernel_config "$cfg"; then + mandatory_cfg_missing=1 + fi +done + +if [ "$mandatory_cfg_missing" -ne 0 ]; then + log_fail "$TESTNAME FAIL - mandatory KVM kernel config is missing" + echo "$TESTNAME FAIL" >"$res_file" + exit 0 +fi + +# Optional configs: log only, do not gate test result. +for cfg in \ + CONFIG_HAVE_KVM \ + CONFIG_HAVE_KVM_IRQCHIP \ + CONFIG_HAVE_KVM_IRQFD \ + CONFIG_KVM_ARM_PMU \ + CONFIG_KVM_GENERIC_DIRTYLOG_READ_PROTECT +do + kvm_log_optional_kernel_config "$cfg" || true +done + +log_info "=== KVM Device Node Validation ===" +kvm_check_device_node +dev_rc=$? + +if [ "$dev_rc" -eq 2 ]; then + log_fail "$TESTNAME FAIL - /dev/kvm is not available" + echo "$TESTNAME FAIL" >"$res_file" + exit 0 +fi + +if [ "$dev_rc" -ne 0 ]; then + log_fail "$TESTNAME FAIL - /dev/kvm node validation failed" + echo "$TESTNAME FAIL" >"$res_file" + exit 0 +fi + +log_info "=== KVM ioctl API Validation ===" +kvm_check_api_version +api_rc=$? + +if [ "$api_rc" -eq 2 ]; then + log_skip "$TESTNAME SKIP - KVM API helper dependency is not available" + echo "$TESTNAME SKIP" >"$res_file" + exit 0 +fi + +if [ "$api_rc" -ne 0 ]; then + log_fail "$TESTNAME FAIL - KVM ioctl API validation failed" + echo "$TESTNAME FAIL" >"$res_file" + exit 0 +fi + +log_info "=== KVM Driver Dmesg Validation ===" +if ! kvm_check_boot_dmesg_errors "$DMESG_DIR"; then + log_fail "$TESTNAME FAIL - fatal KVM/EL2 dmesg errors detected" + echo "$TESTNAME FAIL" >"$res_file" + exit 0 +fi + +log_pass "$TESTNAME PASS - KVM kernel config, /dev/kvm, and ioctl API are valid" +echo "$TESTNAME PASS" >"$res_file" + +log_info "-------------------Completed $TESTNAME Testcase----------------------------" +exit 0 diff --git a/Runner/suites/Virtualization/KVM/KVM_EL2_DTB/KVM_EL2_DTB.yaml b/Runner/suites/Virtualization/KVM/KVM_EL2_DTB/KVM_EL2_DTB.yaml new file mode 100755 index 00000000..9e5f5da2 --- /dev/null +++ b/Runner/suites/Virtualization/KVM/KVM_EL2_DTB/KVM_EL2_DTB.yaml @@ -0,0 +1,17 @@ +metadata: + name: KVM_EL2_DTB + format: "Lava-Test Test Definition 1.0" + description: "Validate dynamic EL2-DTB remoteproc/IOMMU runtime evidence for KVM boot" + maintainer: + - Srikanth kumar + os: + - linux + scope: + - functional + +run: + steps: + - REPO_PATH=$PWD + - cd Runner/suites/Virtualization/KVM/KVM_EL2_DTB + - ./run.sh || true + - $REPO_PATH/Runner/utils/send-to-lava.sh KVM_EL2_DTB.res diff --git a/Runner/suites/Virtualization/KVM/KVM_EL2_DTB/README.md b/Runner/suites/Virtualization/KVM/KVM_EL2_DTB/README.md new file mode 100644 index 00000000..5716d3ef --- /dev/null +++ b/Runner/suites/Virtualization/KVM/KVM_EL2_DTB/README.md @@ -0,0 +1,180 @@ +# KVM_EL2_DTB + +## Overview + +`KVM_EL2_DTB` validates that the running target exposes dynamic EL2-DTB +runtime evidence required for KVM and remoteproc/IOMMU coexistence. + +This test is designed for Qualcomm ARM64 embedded targets where EL2 boot may +use an EL2-specific DTB or overlay. It intentionally avoids maintaining a +per-board allowlist and instead validates the live DT/sysfs evidence. + +The test is non-destructive. It does not stop, start, reset, or otherwise +control any remoteproc instance. + +## Test location + +```text +Runner/suites/Virtualization/KVM/KVM_EL2_DTB/ +``` + +## Files + +```text +run.sh +KVM_EL2_DTB.yaml +README.md +``` + +## Dependencies + +The test uses common helpers from: + +```text +Runner/utils/functestlib.sh +Runner/utils/lib_kvm.sh +``` + +Required target utilities: + +```text +cat grep awk sed find tr mkdir uname +``` + +## Validation coverage + +The test validates: + +1. Mandatory KVM availability gate: + - `CONFIG_KVM` + - `/dev/kvm` exists and is usable + +2. Live device-tree identity logging: + - `/proc/device-tree` + - `/sys/firmware/devicetree/base` + - `model` + - `compatible` + +3. Dynamic remoteproc inspection: + - logs `/sys/class/remoteproc/remoteproc*/name` + - logs `/sys/class/remoteproc/remoteproc*/firmware` + - logs `/sys/class/remoteproc/remoteproc*/state` + - uses existing `functestlib.sh` remoteproc helpers when available + +4. EL2-DTB evidence: + - remoteproc/rproc DT nodes + - remoteproc/rproc `iommus` properties + - runtime sysfs IOMMU/devlink/platform evidence for remoteproc devices + +5. Kernel log scan: + - checks for fatal KVM/EL2/HYP/GIC related runtime errors + +## What counts as EL2 runtime evidence + +The shared `lib_kvm.sh` helper checks for evidence such as: + +```text +/proc/device-tree/...remoteproc.../iommus +/sys/firmware/devicetree/base/...remoteproc.../iommus +/sys/kernel/iommu_groups/*/devices/*remoteproc* +/sys/kernel/iommu_groups/*/devices/*adsp* +/sys/kernel/iommu_groups/*/devices/*cdsp* +/sys/class/devlink/*remoteproc* +/sys/class/devlink/*adsp* +/sys/class/devlink/*cdsp* +/sys/bus/platform/devices/*remoteproc* +``` + +The goal is to detect whether the live boot has the EL2-style remoteproc/IOMMU +layout without hardcoding each board or DTB name. + +## Result policy + +### PASS + +The test reports `PASS` when: + +- `CONFIG_KVM` is enabled, +- `/dev/kvm` is available and usable, +- remoteproc/IOMMU EL2 runtime evidence is found, +- no fatal KVM/EL2/HYP related dmesg errors are detected. + +### SKIP + +The test reports `SKIP` when: + +- no remoteproc DT/sysfs entries exist, so EL2 remoteproc validation is not + applicable on this target, +- required userspace utilities are missing, +- the testcase path or setup environment cannot be resolved before test + execution starts. + +### FAIL + +The test reports `FAIL` when: + +- `CONFIG_KVM` is not enabled, +- `/dev/kvm` is not present, +- `/dev/kvm` exists but is not usable, +- remoteproc entries exist but no EL2-style DT/sysfs evidence is found, +- fatal KVM/EL2/HYP related errors are detected in kernel logs. + +## Manual execution + +From the repository root on target: + +```sh +cd Runner/suites/Virtualization/KVM/KVM_EL2_DTB +./run.sh +cat KVM_EL2_DTB.res +``` + +Expected result file: + +```text +KVM_EL2_DTB PASS +``` + +or: + +```text +KVM_EL2_DTB SKIP +``` + +or: + +```text +KVM_EL2_DTB FAIL +``` + +## LAVA execution + +The YAML file runs: + +```sh +cd Runner/suites/Virtualization/KVM/KVM_EL2_DTB +./run.sh || true +$REPO_PATH/Runner/utils/send-to-lava.sh KVM_EL2_DTB.res +``` + +## Logs + +The test creates logs under: + +```text +results/KVM_EL2_DTB/ +``` + +KVM/EL2 dmesg logs are captured under: + +```text +results/KVM_EL2_DTB/dmesg/ +``` + +## Notes + +This test does not duplicate secure PIL or remoteproc lifecycle validation. +Dedicated remoteproc tests should continue to cover stop/start/reset flows. + +`KVM_EL2_DTB` only validates that the live EL2-compatible DT/runtime layout is +present when KVM is available. diff --git a/Runner/suites/Virtualization/KVM/KVM_EL2_DTB/run.sh b/Runner/suites/Virtualization/KVM/KVM_EL2_DTB/run.sh new file mode 100755 index 00000000..e687b1eb --- /dev/null +++ b/Runner/suites/Virtualization/KVM/KVM_EL2_DTB/run.sh @@ -0,0 +1,134 @@ +#!/bin/sh +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause + +TESTNAME="KVM_EL2_DTB" + +# Robustly find and source init_env +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +INIT_ENV="" +SEARCH="$SCRIPT_DIR" + +while [ "$SEARCH" != "/" ]; do + if [ -f "$SEARCH/init_env" ]; then + INIT_ENV="$SEARCH/init_env" + break + fi + SEARCH=$(dirname "$SEARCH") +done + +RES_FALLBACK="$SCRIPT_DIR/${TESTNAME}.res" + +if [ -z "$INIT_ENV" ]; then + echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + echo "$TESTNAME SKIP" >"$RES_FALLBACK" 2>/dev/null || true + exit 0 +fi + +# Only source if not already loaded (idempotent) +if [ -z "${__INIT_ENV_LOADED:-}" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" + __INIT_ENV_LOADED=1 +fi + +# Always source functestlib.sh, using $TOOLS exported by init_env +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/functestlib.sh" + +# Source KVM helper library +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/lib_kvm.sh" + +test_path=$(find_test_case_by_name "$TESTNAME") +if [ -z "$test_path" ] || [ ! -d "$test_path" ]; then + log_skip "$TESTNAME SKIP - test path not found" + echo "$TESTNAME SKIP" >"$RES_FALLBACK" 2>/dev/null || true + exit 0 +fi + +if ! cd "$test_path"; then + log_skip "$TESTNAME SKIP - cannot cd into $test_path" + echo "$TESTNAME SKIP" >"$RES_FALLBACK" 2>/dev/null || true + exit 0 +fi + +# shellcheck disable=SC2034 +res_file="./$TESTNAME.res" +RESULT_DIR="./results/$TESTNAME" +DMESG_DIR="$RESULT_DIR/dmesg" + +mkdir -p "$DMESG_DIR" 2>/dev/null || true + +log_info "-----------------------------------------------------------------------------------------" +log_info "-------------------Starting $TESTNAME Testcase----------------------------" +log_info "=== Test Initialization ===" + +deps_list="cat grep awk sed find tr mkdir uname" +log_info "Checking dependencies: $deps_list" +if ! check_dependencies "$deps_list"; then + log_skip "$TESTNAME SKIP - missing one or more dependencies: $deps_list" + echo "$TESTNAME SKIP" >"$res_file" + exit 0 +fi + +if command -v detect_platform >/dev/null 2>&1; then + detect_platform >/dev/null 2>&1 || true + log_info "Platform Details: machine='${PLATFORM_MACHINE:-unknown}' target='${PLATFORM_TARGET:-unknown}' kernel='${PLATFORM_KERNEL:-}' arch='${PLATFORM_ARCH:-}'" +else + log_info "Platform Details: kernel='$(uname -r 2>/dev/null || echo unknown)' arch='$(uname -m 2>/dev/null || echo unknown)'" +fi + +log_info "=== KVM Availability Gate ===" + +if ! check_kernel_config "CONFIG_KVM"; then + log_fail "$TESTNAME FAIL - mandatory KVM kernel config CONFIG_KVM is not enabled" + echo "$TESTNAME FAIL" >"$res_file" + exit 0 +fi + +kvm_check_device_node +dev_rc=$? + +if [ "$dev_rc" -eq 2 ]; then + log_fail "$TESTNAME FAIL - /dev/kvm is not available" + echo "$TESTNAME FAIL" >"$res_file" + exit 0 +fi + +if [ "$dev_rc" -ne 0 ]; then + log_fail "$TESTNAME FAIL - /dev/kvm exists but is not usable" + echo "$TESTNAME FAIL" >"$res_file" + exit 0 +fi + +log_info "=== Dynamic EL2-DTB Runtime Evidence Validation ===" +log_info "This test performs log-only remoteproc inspection and does not stop/start/reset remoteprocs." + +kvm_check_el2_runtime_evidence +el2_rc=$? + +if [ "$el2_rc" -eq 2 ]; then + log_skip "$TESTNAME SKIP - no remoteproc DT/sysfs entries found; EL2-DTB validation is not applicable" + echo "$TESTNAME SKIP" >"$res_file" + exit 0 +fi + +if [ "$el2_rc" -ne 0 ]; then + log_fail "$TESTNAME FAIL - EL2-DTB remoteproc/IOMMU runtime evidence is missing" + echo "$TESTNAME FAIL" >"$res_file" + exit 0 +fi + +log_info "=== EL2/KVM Dmesg Validation ===" +if ! kvm_check_boot_dmesg_errors "$DMESG_DIR"; then + log_fail "$TESTNAME FAIL - fatal KVM/EL2 dmesg errors detected" + echo "$TESTNAME FAIL" >"$res_file" + exit 0 +fi + +log_pass "$TESTNAME PASS - dynamic EL2-DTB evidence is valid" +echo "$TESTNAME PASS" >"$res_file" + +log_info "-------------------Completed $TESTNAME Testcase----------------------------" +exit 0 diff --git a/Runner/suites/Virtualization/KVM/KVM_Infra/KVM_Infra.yaml b/Runner/suites/Virtualization/KVM/KVM_Infra/KVM_Infra.yaml new file mode 100755 index 00000000..2492bb2c --- /dev/null +++ b/Runner/suites/Virtualization/KVM/KVM_Infra/KVM_Infra.yaml @@ -0,0 +1,17 @@ +metadata: + name: KVM_Infra + format: "Lava-Test Test Definition 1.0" + description: "Validate QEMU/KVM host infrastructure and KVM acceleration availability" + maintainer: + - Srikanth kumar + os: + - linux + scope: + - functional + +run: + steps: + - REPO_PATH=$PWD + - cd Runner/suites/Virtualization/KVM/KVM_Infra + - ./run.sh || true + - $REPO_PATH/Runner/utils/send-to-lava.sh KVM_Infra.res diff --git a/Runner/suites/Virtualization/KVM/KVM_Infra/README.md b/Runner/suites/Virtualization/KVM/KVM_Infra/README.md new file mode 100644 index 00000000..f2112a25 --- /dev/null +++ b/Runner/suites/Virtualization/KVM/KVM_Infra/README.md @@ -0,0 +1,187 @@ +# KVM_Infra + +## Overview + +`KVM_Infra` validates the host-side userspace infrastructure required to run +virtual machines with KVM acceleration. + +This test does not boot a guest VM. It verifies that KVM is available, a QEMU +system emulator exists, QEMU advertises KVM acceleration, and optional VM host +networking acceleration paths are visible when present. + +## Test location + +```text +Runner/suites/Virtualization/KVM/KVM_Infra/ +``` + +## Files + +```text +run.sh +KVM_Infra.yaml +README.md +``` + +## Dependencies + +The test uses common helpers from: + +```text +Runner/utils/functestlib.sh +Runner/utils/lib_kvm.sh +``` + +Required target utilities: + +```text +cat grep awk sed find tr head mkdir uname +``` + +Optional runtime tools: + +```text +qemu-system-aarch64 +qemu-system-arm +qemu-kvm +qemu-img +``` + +The test searches for a usable QEMU system emulator in this order: + +```text +qemu-system-aarch64 +qemu-system-arm +qemu-kvm +``` + +## Validation coverage + +The test validates: + +1. Mandatory KVM availability gate: + - `CONFIG_KVM` + - `/dev/kvm` exists and is usable + +2. QEMU binary availability: + - finds a QEMU system emulator suitable for the target + - logs QEMU version + - logs machine help output + - logs CPU help output + +3. QEMU KVM acceleration: + - runs `qemu-system-* -accel help` + - checks whether `kvm` acceleration is advertised + +4. Optional host infrastructure: + - `/dev/net/tun` + - `/dev/vhost-net` + - loaded `vhost_net` module + +5. Kernel log scan: + - checks for fatal KVM/EL2/HYP/GIC related runtime errors + +## Result policy + +### PASS + +The test reports `PASS` when: + +- `CONFIG_KVM` is enabled, +- `/dev/kvm` is available and usable, +- QEMU system binary is found, +- QEMU advertises KVM acceleration, +- no fatal KVM/EL2 errors are detected in kernel logs. + +### SKIP + +The test reports `SKIP` when: + +- no QEMU system emulator is installed, +- required userspace utilities are missing, +- the testcase path or setup environment cannot be resolved before test + execution starts. + +This keeps the test CI-safe for minimal images that intentionally do not ship +QEMU. + +### FAIL + +The test reports `FAIL` when: + +- `CONFIG_KVM` is not enabled, +- `/dev/kvm` is not present, +- `/dev/kvm` exists but is not usable, +- QEMU is installed but does not advertise KVM acceleration, +- fatal KVM/EL2/HYP related errors are detected in kernel logs. + +## Optional infrastructure behavior + +Missing `/dev/net/tun` or `vhost-net` is logged as a warning only. These are +not hard requirements for confirming QEMU/KVM host infrastructure, because a +guest may still boot with limited or alternate networking. + +## Manual execution + +From the repository root on target: + +```sh +cd Runner/suites/Virtualization/KVM/KVM_Infra +./run.sh +cat KVM_Infra.res +``` + +Expected result file: + +```text +KVM_Infra PASS +``` + +or: + +```text +KVM_Infra SKIP +``` + +or: + +```text +KVM_Infra FAIL +``` + +## LAVA execution + +The YAML file runs: + +```sh +cd Runner/suites/Virtualization/KVM/KVM_Infra +./run.sh || true +$REPO_PATH/Runner/utils/send-to-lava.sh KVM_Infra.res +``` + +## Logs + +The test creates logs under: + +```text +results/KVM_Infra/ +``` + +KVM/EL2 dmesg logs are captured under: + +```text +results/KVM_Infra/dmesg/ +``` + +## Notes + +This test only validates QEMU/KVM host infrastructure. Actual guest boot is +covered by the later `QEMU_VM_Validation` test. + +Related tests: + +```text +KVM_Driver +KVM_EL2_DTB +QEMU_VM_Validation +``` diff --git a/Runner/suites/Virtualization/KVM/KVM_Infra/run.sh b/Runner/suites/Virtualization/KVM/KVM_Infra/run.sh new file mode 100755 index 00000000..6091edb6 --- /dev/null +++ b/Runner/suites/Virtualization/KVM/KVM_Infra/run.sh @@ -0,0 +1,148 @@ +#!/bin/sh +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause + +TESTNAME="KVM_Infra" + +# Robustly find and source init_env +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +INIT_ENV="" +SEARCH="$SCRIPT_DIR" + +while [ "$SEARCH" != "/" ]; do + if [ -f "$SEARCH/init_env" ]; then + INIT_ENV="$SEARCH/init_env" + break + fi + SEARCH=$(dirname "$SEARCH") +done + +RES_FALLBACK="$SCRIPT_DIR/${TESTNAME}.res" + +if [ -z "$INIT_ENV" ]; then + echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + echo "$TESTNAME SKIP" >"$RES_FALLBACK" 2>/dev/null || true + exit 0 +fi + +# Only source if not already loaded (idempotent) +if [ -z "${__INIT_ENV_LOADED:-}" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" + __INIT_ENV_LOADED=1 +fi + +# Always source functestlib.sh, using $TOOLS exported by init_env +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/functestlib.sh" + +# Source KVM helper library +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/lib_kvm.sh" + +test_path=$(find_test_case_by_name "$TESTNAME") +if [ -z "$test_path" ] || [ ! -d "$test_path" ]; then + log_skip "$TESTNAME SKIP - test path not found" + echo "$TESTNAME SKIP" >"$RES_FALLBACK" 2>/dev/null || true + exit 0 +fi + +if ! cd "$test_path"; then + log_skip "$TESTNAME SKIP - cannot cd into $test_path" + echo "$TESTNAME SKIP" >"$RES_FALLBACK" 2>/dev/null || true + exit 0 +fi + +# shellcheck disable=SC2034 +res_file="./$TESTNAME.res" +RESULT_DIR="./results/$TESTNAME" +DMESG_DIR="$RESULT_DIR/dmesg" + +mkdir -p "$DMESG_DIR" 2>/dev/null || true + +log_info "-----------------------------------------------------------------------------------------" +log_info "-------------------Starting $TESTNAME Testcase----------------------------" +log_info "=== Test Initialization ===" + +deps_list="cat grep awk sed find tr head mkdir uname" +log_info "Checking dependencies: $deps_list" +if ! check_dependencies "$deps_list"; then + log_skip "$TESTNAME SKIP - missing one or more dependencies: $deps_list" + echo "$TESTNAME SKIP" >"$res_file" + exit 0 +fi + +if command -v detect_platform >/dev/null 2>&1; then + detect_platform >/dev/null 2>&1 || true + log_info "Platform Details: machine='${PLATFORM_MACHINE:-unknown}' target='${PLATFORM_TARGET:-unknown}' kernel='${PLATFORM_KERNEL:-}' arch='${PLATFORM_ARCH:-}'" +else + log_info "Platform Details: kernel='$(uname -r 2>/dev/null || echo unknown)' arch='$(uname -m 2>/dev/null || echo unknown)'" +fi + +log_info "=== KVM Availability Gate ===" + +if ! check_kernel_config "CONFIG_KVM"; then + log_fail "$TESTNAME FAIL - mandatory KVM kernel config CONFIG_KVM is not enabled" + echo "$TESTNAME FAIL" >"$res_file" + exit 0 +fi + +kvm_check_device_node +dev_rc=$? + +if [ "$dev_rc" -eq 2 ]; then + log_fail "$TESTNAME FAIL - /dev/kvm is not available" + echo "$TESTNAME FAIL" >"$res_file" + exit 0 +fi + +if [ "$dev_rc" -ne 0 ]; then + log_fail "$TESTNAME FAIL - /dev/kvm exists but is not usable" + echo "$TESTNAME FAIL" >"$res_file" + exit 0 +fi + +log_info "=== QEMU Binary Detection ===" + +QEMU_BIN="$(kvm_find_qemu_binary 2>/dev/null || true)" +if [ -z "$QEMU_BIN" ]; then + log_skip "$TESTNAME SKIP - qemu-system binary is not installed" + echo "$TESTNAME SKIP" >"$res_file" + exit 0 +fi + +log_pass "QEMU binary found: $QEMU_BIN" + +QEMU_IMG="$(kvm_find_qemu_img 2>/dev/null || true)" +if [ -n "$QEMU_IMG" ]; then + log_pass "qemu-img found: $QEMU_IMG" +else + log_warn "qemu-img not found" +fi + +log_info "=== QEMU Capability Probe ===" +kvm_qemu_probe "$QEMU_BIN" || true + +log_info "=== QEMU KVM Acceleration Check ===" +if ! kvm_check_qemu_kvm_accel "$QEMU_BIN"; then + log_fail "$TESTNAME FAIL - QEMU does not advertise KVM acceleration" + echo "$TESTNAME FAIL" >"$res_file" + exit 0 +fi + +log_info "=== Optional VM Host Device Infrastructure ===" +kvm_check_tun_device || true +kvm_check_vhost_net || true + +log_info "=== KVM/QEMU Dmesg Validation ===" +if ! kvm_check_boot_dmesg_errors "$DMESG_DIR"; then + log_fail "$TESTNAME FAIL - fatal KVM/EL2 dmesg errors detected" + echo "$TESTNAME FAIL" >"$res_file" + exit 0 +fi + +log_pass "$TESTNAME PASS - QEMU/KVM host infrastructure is available" +echo "$TESTNAME PASS" >"$res_file" + +log_info "-------------------Completed $TESTNAME Testcase----------------------------" +exit 0 diff --git a/Runner/utils/lib_kvm.sh b/Runner/utils/lib_kvm.sh new file mode 100755 index 00000000..1ca0585b --- /dev/null +++ b/Runner/utils/lib_kvm.sh @@ -0,0 +1,619 @@ +#!/bin/sh +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause +# +# Common KVM/virtualization validation helpers. +# +# This library must be sourced after functestlib.sh. +# +# Reuse from functestlib.sh: +# log_info/log_pass/log_fail/log_skip/log_warn +# check_dependencies +# check_kernel_config +# scan_dmesg_errors +# get_kernel_log +# detect_platform +# get_remoteproc_by_firmware, when available +# +# Return-code convention: +# 0 = validation passed / evidence found +# 1 = validation failed +# 2 = validation not applicable or dependency missing; caller should SKIP + +# Validate /dev/kvm presence, type, and access permissions. +# +# Returns: +# 0 if /dev/kvm is present, character device, readable, and writable +# 1 if /dev/kvm exists but is not usable +# 2 if /dev/kvm is not present +kvm_check_device_node() { + if [ ! -e /dev/kvm ]; then + log_skip "/dev/kvm is not present" + return 2 + fi + + if [ ! -c /dev/kvm ]; then + log_fail "/dev/kvm exists but is not a character device" + return 1 + fi + + if [ ! -r /dev/kvm ]; then + log_fail "/dev/kvm exists but is not readable" + return 1 + fi + + if [ ! -w /dev/kvm ]; then + log_fail "/dev/kvm exists but is not writable" + return 1 + fi + + log_pass "/dev/kvm is present and accessible" + return 0 +} + +# Validate the KVM userspace API through /dev/kvm ioctl calls. +# +# Checks: +# - open("/dev/kvm") succeeds +# - KVM_GET_API_VERSION returns 12 +# - KVM_CREATE_VM succeeds +# +# Notes: +# Uses python3 to avoid requiring a compiled helper binary on target. +# +# Returns: +# 0 if KVM API validation passes +# 1 if /dev/kvm API validation fails +# 2 if python3 is unavailable +kvm_check_api_version() { + if ! command -v python3 >/dev/null 2>&1; then + log_skip "python3 is not available; cannot run /dev/kvm ioctl API check" + return 2 + fi + + python3 - <<'PY' +import fcntl +import os +import sys + +KVM_GET_API_VERSION = 0xAE00 +KVM_CREATE_VM = 0xAE01 +EXPECTED_API = 12 + +try: + fd = os.open("/dev/kvm", os.O_RDWR | getattr(os, "O_CLOEXEC", 0)) +except OSError as exc: + print("open(/dev/kvm) failed: %s" % exc) + sys.exit(1) + +try: + api = fcntl.ioctl(fd, KVM_GET_API_VERSION, 0) +except OSError as exc: + print("KVM_GET_API_VERSION failed: %s" % exc) + os.close(fd) + sys.exit(1) + +if api != EXPECTED_API: + print("Unexpected KVM API version: got=%s expected=%s" % (api, EXPECTED_API)) + os.close(fd) + sys.exit(1) + +try: + vmfd = fcntl.ioctl(fd, KVM_CREATE_VM, 0) +except OSError as exc: + print("KVM_CREATE_VM failed: %s" % exc) + os.close(fd) + sys.exit(1) + +try: + os.close(vmfd) +except OSError: + pass + +os.close(fd) +print("KVM API check passed: KVM_GET_API_VERSION=%s KVM_CREATE_VM=ok" % api) +sys.exit(0) +PY + rc=$? + + if [ "$rc" -eq 0 ]; then + log_pass "/dev/kvm ioctl API validation passed" + return 0 + fi + + log_fail "/dev/kvm ioctl API validation failed" + return 1 +} + +# Scan kernel logs for fatal KVM/EL2/HYP/GIC virtualization errors. +# +# Args: +# $1 - output directory where dmesg logs should be collected +# +# Behavior: +# Uses scan_dmesg_errors from functestlib.sh when available. +# Falls back to get_kernel_log/dmesg capture otherwise. +# +# Returns: +# 0 if no fatal error is detected +# 1 if fatal KVM/EL2-related messages are detected +kvm_check_boot_dmesg_errors() { + outdir="$1" + include_regex='kvm|KVM|hyp|HYP|el2|EL2|vgic|VGIC|gicv|GICV' + exclude_regex='not found|dummy regulator|probe deferred|using dummy regulator|EEXIST' + + mkdir -p "$outdir" 2>/dev/null || true + + if command -v scan_dmesg_errors >/dev/null 2>&1; then + scan_dmesg_errors "$outdir" "$include_regex" "$exclude_regex" || true + + if [ -s "$outdir/dmesg_errors.log" ]; then + if grep -Ei 'fail|failed|error|disabled|unavailable|not in hyp|hyp mode|EL2.*unavailable|HYP.*unavailable|KVM.*not available|kvm.*not available|panic|BUG|Oops' "$outdir/dmesg_errors.log" >/dev/null 2>&1; then + log_fail "Fatal KVM/EL2 related messages found in dmesg: $outdir/dmesg_errors.log" + return 1 + fi + + log_warn "KVM/EL2 related dmesg messages found but no fatal pattern matched: $outdir/dmesg_errors.log" + return 0 + fi + + log_info "No relevant KVM/EL2 dmesg errors found." + return 0 + fi + + if command -v get_kernel_log >/dev/null 2>&1; then + get_kernel_log >"$outdir/dmesg_snapshot.log" 2>/dev/null || true + elif command -v dmesg >/dev/null 2>&1; then + dmesg >"$outdir/dmesg_snapshot.log" 2>/dev/null || true + else + log_warn "No kernel log source available for KVM dmesg validation" + return 0 + fi + + if grep -Ei 'KVM|kvm|HYP|hyp|EL2|el2' "$outdir/dmesg_snapshot.log" >/dev/null 2>&1; then + grep -Ei 'KVM|kvm|HYP|hyp|EL2|el2' "$outdir/dmesg_snapshot.log" >"$outdir/kvm_dmesg.log" 2>/dev/null || true + log_info "KVM/EL2 dmesg lines captured: $outdir/kvm_dmesg.log" + + if grep -Ei 'fail|failed|error|disabled|unavailable|not in hyp|hyp mode|EL2.*unavailable|HYP.*unavailable|KVM.*not available|kvm.*not available|panic|BUG|Oops' "$outdir/kvm_dmesg.log" >/dev/null 2>&1; then + log_fail "Fatal KVM/EL2 related messages found in dmesg: $outdir/kvm_dmesg.log" + return 1 + fi + else + log_warn "No KVM/EL2 dmesg lines found" + fi + + return 0 +} + +# Print available live device-tree root paths. +# +# Device-tree may be exposed through either: +# /proc/device-tree +# /sys/firmware/devicetree/base +# +# Returns: +# Prints existing DT roots to stdout +kvm_dt_roots() { + for root in /proc/device-tree /sys/firmware/devicetree/base; do + if [ -d "$root" ]; then + printf '%s\n' "$root" + fi + done +} + +# Log live device-tree identity information. +# +# Logs: +# - DT root path +# - model +# - compatible +# +# This helper is logging-only. +# +# Returns: +# Always returns 0 +kvm_log_dt_identity() { + for root in $(kvm_dt_roots); do + log_info "DT root: $root" + + if [ -r "$root/model" ]; then + model="$(tr '\000' ' ' <"$root/model" 2>/dev/null)" + log_info "DT model: $model" + fi + + if [ -r "$root/compatible" ]; then + compat="$(tr '\000' ' ' <"$root/compatible" 2>/dev/null)" + log_info "DT compatible: $compat" + fi + done + + return 0 +} + +# Find likely remoteproc/rproc nodes from the live device tree. +# +# Returns: +# Prints matching DT node paths to stdout. +kvm_dt_find_remoteproc_nodes() { + for root in $(kvm_dt_roots); do + find "$root" -type d 2>/dev/null | grep -Ei '/([^/]*remoteproc[^/]*|[^/]*rproc[^/]*)$' || true + done +} + +# Check whether live DT has remoteproc IOMMU evidence. +# +# Checks: +# - remoteproc/rproc DT nodes exist +# - at least one matching node or path has an iommus property +# +# Returns: +# 0 if remoteproc DT IOMMU evidence is found +# 1 if remoteproc DT nodes exist but no IOMMU evidence is found +# 2 if no remoteproc DT nodes are found +kvm_dt_remoteproc_has_iommus() { + node_file="/tmp/kvm_remoteproc_nodes.$$" + iommu_file="/tmp/kvm_remoteproc_iommus.$$" + node_count=0 + iommu_count=0 + + rm -f "$node_file" "$iommu_file" 2>/dev/null || true + + kvm_dt_find_remoteproc_nodes >"$node_file" 2>/dev/null || true + + if [ -s "$node_file" ]; then + while IFS= read -r node; do + [ -n "$node" ] || continue + node_count=$((node_count + 1)) + log_info "[EL2-DT] remoteproc DT node: $node" + + if [ -e "$node/iommus" ]; then + iommu_count=$((iommu_count + 1)) + log_info "[EL2-DT] remoteproc DT iommus present: $node/iommus" + fi + done <"$node_file" + fi + + for root in $(kvm_dt_roots); do + find "$root" -type f -name iommus 2>/dev/null | grep -Ei 'remoteproc|rproc|adsp|cdsp|gpdsp|wpss' || true + done >"$iommu_file" 2>/dev/null || true + + if [ -s "$iommu_file" ]; then + while IFS= read -r found_file; do + [ -n "$found_file" ] || continue + iommu_count=$((iommu_count + 1)) + log_info "[EL2-DT] remoteproc/iommus evidence: $found_file" + done <"$iommu_file" + fi + + rm -f "$node_file" "$iommu_file" 2>/dev/null || true + + if [ "$node_count" -eq 0 ]; then + return 2 + fi + + if [ "$iommu_count" -gt 0 ]; then + return 0 + fi + + return 1 +} + +# Log remoteproc instances using sysfs and functestlib.sh helpers when present. +# +# This helper is logging-only. It must not stop, start, or reset remoteprocs. +# +# Returns: +# 0 if at least one remoteproc instance is logged +# 2 if no remoteproc entries are found +kvm_log_remoteproc_state_summary() { + found=0 + entries="" + fw="" + rpath="" + rstate="" + rfirm="" + rname="" + + for rproc in /sys/class/remoteproc/remoteproc*; do + [ -e "$rproc" ] || continue + found=1 + + fw="$(cat "$rproc/firmware" 2>/dev/null || echo unknown)" + rstate="$(cat "$rproc/state" 2>/dev/null || echo unknown)" + rname="$(cat "$rproc/name" 2>/dev/null || echo unknown)" + + log_info "[remoteproc] path=$rproc name=$rname firmware=$fw state=$rstate" + done + + if command -v get_remoteproc_by_firmware >/dev/null 2>&1; then + for fw in adsp cdsp cdsp0 cdsp1 gpdsp gpdsp0 gpdsp1 wpss modem; do + entries="$(get_remoteproc_by_firmware "$fw" "" all 2>/dev/null || true)" + [ -n "$entries" ] || continue + + while IFS='|' read -r rpath rstate rfirm rname; do + [ -n "$rpath" ] || continue + found=1 + log_info "[remoteproc-helper] fw=$fw path=$rpath state=$rstate firmware=$rfirm name=$rname" + done <<__KVM_RPROC_ENTRIES__ +$entries +__KVM_RPROC_ENTRIES__ + done + fi + + if [ "$found" -eq 0 ]; then + log_info "No /sys/class/remoteproc entries found" + return 2 + fi + + return 0 +} + +# Check runtime sysfs evidence that remoteproc devices are connected through +# IOMMU/devlink/platform relationships. +# +# This is used as dynamic EL2-DTB evidence and avoids hardcoding board names. +# +# Returns: +# 0 if at least one sysfs evidence path is found +# 1 if no evidence path is found +kvm_sysfs_remoteproc_iommu_evidence() { + evidence_count=0 + + for path in \ + /sys/kernel/iommu_groups/*/devices/*remoteproc* \ + /sys/kernel/iommu_groups/*/devices/*rproc* \ + /sys/kernel/iommu_groups/*/devices/*adsp* \ + /sys/kernel/iommu_groups/*/devices/*cdsp* \ + /sys/kernel/iommu_groups/*/devices/*gpdsp* \ + /sys/class/devlink/*remoteproc* \ + /sys/class/devlink/*rproc* \ + /sys/class/devlink/*adsp* \ + /sys/class/devlink/*cdsp* \ + /sys/class/devlink/*gpdsp* \ + /sys/devices/platform/*/*remoteproc* \ + /sys/devices/platform/*/*rproc* \ + /sys/bus/platform/devices/*remoteproc* \ + /sys/bus/platform/devices/*rproc* + do + [ -e "$path" ] || continue + evidence_count=$((evidence_count + 1)) + log_info "[EL2-SYSFS] remoteproc/IOMMU/devlink evidence: $path" + done + + if [ "$evidence_count" -gt 0 ]; then + return 0 + fi + + return 1 +} + +# Validate dynamic EL2-DTB runtime evidence. +# +# This helper reuses functestlib.sh remoteproc discovery when available, but it +# does not duplicate secure PIL or remoteproc lifecycle validation. +# +# Checks: +# - live DT identity +# - remoteproc state summary +# - remoteproc DT iommus evidence +# - remoteproc sysfs IOMMU/devlink/platform evidence +# +# Returns: +# 0 if EL2 remoteproc/IOMMU runtime evidence is found +# 1 if remoteproc exists but EL2-style evidence is missing +# 2 if no remoteproc DT/sysfs entries exist, so validation is not applicable +kvm_check_el2_runtime_evidence() { + dt_rc=1 + sysfs_rc=1 + rproc_rc=2 + + kvm_log_dt_identity + + kvm_log_remoteproc_state_summary + rproc_rc=$? + + kvm_dt_remoteproc_has_iommus + dt_rc=$? + + kvm_sysfs_remoteproc_iommu_evidence + sysfs_rc=$? + + if [ "$rproc_rc" -eq 2 ] && [ "$dt_rc" -eq 2 ]; then + log_info "No remoteproc DT/sysfs entries found; EL2 remoteproc validation is not applicable." + return 2 + fi + + if [ "$dt_rc" -eq 0 ] || [ "$sysfs_rc" -eq 0 ]; then + log_pass "EL2 remoteproc/IOMMU runtime evidence found." + return 0 + fi + + log_fail "Remoteproc entries exist, but no EL2 remoteproc/IOMMU runtime evidence was found." + return 1 +} + +# Locate a QEMU system emulator suitable for KVM validation. +# +# Search order: +# qemu-system-aarch64 +# qemu-system-arm +# qemu-kvm +# +# Returns: +# 0 and prints binary path if found +# 1 if no suitable QEMU binary is found +kvm_find_qemu_binary() { + for bin in qemu-system-aarch64 qemu-system-arm qemu-kvm; do + if command -v "$bin" >/dev/null 2>&1; then + command -v "$bin" + return 0 + fi + done + + return 1 +} + +# Locate qemu-img. +# +# Returns: +# 0 and prints binary path if found +# 1 if qemu-img is not found +kvm_find_qemu_img() { + if command -v qemu-img >/dev/null 2>&1; then + command -v qemu-img + return 0 + fi + + return 1 +} + +# Validate whether QEMU advertises KVM acceleration support. +# +# Args: +# $1 - QEMU system binary path +# +# Returns: +# 0 if "kvm" is listed in "-accel help" +# 1 otherwise +kvm_check_qemu_kvm_accel() { + qemu_bin="$1" + accel_log="/tmp/kvm_qemu_accel_help.$$" + + [ -n "$qemu_bin" ] || return 1 + + "$qemu_bin" -accel help >"$accel_log" 2>&1 + rc=$? + + if [ "$rc" -ne 0 ]; then + log_warn "$qemu_bin -accel help failed" + while IFS= read -r line; do + log_warn "[qemu-accel] $line" + done <"$accel_log" + rm -f "$accel_log" 2>/dev/null || true + return 1 + fi + + while IFS= read -r line; do + log_info "[qemu-accel] $line" + done <"$accel_log" + + if grep -Ei '(^|[[:space:]])kvm($|[[:space:]])' "$accel_log" >/dev/null 2>&1; then + rm -f "$accel_log" 2>/dev/null || true + log_pass "QEMU advertises KVM acceleration" + return 0 + fi + + rm -f "$accel_log" 2>/dev/null || true + log_fail "QEMU does not advertise KVM acceleration" + return 1 +} + +# Log basic QEMU version, machine, and CPU capability information. +# +# Args: +# $1 - QEMU system binary path +# +# This helper is logging-only and should not fail the test by itself. +# +# Returns: +# 0 if called with a non-empty binary path +# 1 if no binary path is provided +kvm_qemu_probe() { + qemu_bin="$1" + machine_log="/tmp/kvm_qemu_machine_help.$$" + cpu_log="/tmp/kvm_qemu_cpu_help.$$" + + [ -n "$qemu_bin" ] || return 1 + + "$qemu_bin" -version 2>&1 | while IFS= read -r line; do + log_info "[qemu-version] $line" + done + + "$qemu_bin" -machine help >"$machine_log" 2>&1 || true + head -n 20 "$machine_log" 2>/dev/null | while IFS= read -r line; do + log_info "[qemu-machine] $line" + done + rm -f "$machine_log" 2>/dev/null || true + + "$qemu_bin" -cpu help >"$cpu_log" 2>&1 || true + head -n 20 "$cpu_log" 2>/dev/null | while IFS= read -r line; do + log_info "[qemu-cpu] $line" + done + rm -f "$cpu_log" 2>/dev/null || true + + return 0 +} + +# Validate /dev/net/tun availability for VM networking. +# +# This is optional infrastructure. Missing tun should warn, not fail, +# unless the caller explicitly requires networking. +# +# Returns: +# 0 if /dev/net/tun exists +# 1 otherwise +kvm_check_tun_device() { + if [ -c /dev/net/tun ]; then + log_pass "/dev/net/tun is present" + return 0 + fi + + log_warn "/dev/net/tun is not present; VM networking may be limited" + return 1 +} + +# Validate vhost-net availability for accelerated virtio-net. +# +# This is optional infrastructure. Missing vhost-net should warn, not fail, +# unless the caller explicitly requires accelerated networking. +# +# Returns: +# 0 if /dev/vhost-net exists or vhost_net module is loaded +# 1 otherwise +kvm_check_vhost_net() { + if [ -c /dev/vhost-net ]; then + log_pass "/dev/vhost-net is present" + return 0 + fi + + if grep -qw vhost_net /proc/modules 2>/dev/null; then + log_pass "vhost_net module is loaded" + return 0 + fi + + log_warn "vhost-net is not available; virtio-net acceleration may be limited" + return 1 +} + +# Log optional KVM kernel config without emitting FAIL on missing configs. +# +# Args: +# $1 - kernel config name +# +# Returns: +# 0 if config is enabled as y/m +# 1 if config is missing or config source is unavailable +kvm_log_optional_kernel_config() { + cfg="$1" + + if [ -z "$cfg" ]; then + return 1 + fi + + if [ -r /proc/config.gz ]; then + if command -v zgrep >/dev/null 2>&1; then + if zgrep -Eq "^${cfg}=(y|m)$" /proc/config.gz 2>/dev/null; then + log_info "Optional KVM config $cfg is enabled" + return 0 + fi + elif command -v gzip >/dev/null 2>&1; then + if gzip -dc /proc/config.gz 2>/dev/null | grep -Eq "^${cfg}=(y|m)$"; then + log_info "Optional KVM config $cfg is enabled" + return 0 + fi + fi + fi + + log_warn "Optional KVM config not enabled or not visible: $cfg" + return 1 +}