diff --git a/Runner/config/pkg_command_map.conf b/Runner/config/pkg_command_map.conf index c7e2d8e3d..d2a2705e6 100755 --- a/Runner/config/pkg_command_map.conf +++ b/Runner/config/pkg_command_map.conf @@ -212,6 +212,31 @@ opkg:Sensors:sns_test=qcom-sensors-api qcom-sensors-core qcom-sensors-registry q debian:package-set:fastrpc=fastrpc-support fastrpc-tests ubuntu:package-set:fastrpc=fastrpc-support fastrpc-tests +# --------------------------------------------------------------------------- +# Qualcomm MinkIPC runtime and test packages. +# +# Debian and Ubuntu MinkIPC suites automatically recover this package set from +# qli-staging before validation. The current qli-staging packages are published +# in the trixie suite for both apt-based test images. +# +# The package set intentionally contains only runtime components used by the +# tests. Development files, debug symbols, the IDL compiler, and rpmb_client +# are not required. +# --------------------------------------------------------------------------- + +debian:package-set:minkipc=libminkadaptor0 libminkteec1 minkipc-qteesupplicant minkipc-tests +ubuntu:package-set:minkipc=libminkadaptor0 libminkteec1 minkipc-qteesupplicant minkipc-tests + +debian:smcinvoke_client=minkipc-tests +debian:gp_test_client=minkipc-tests +debian:qtee_supplicant=minkipc-qteesupplicant +debian:sfs_config=minkipc-qteesupplicant + +ubuntu:smcinvoke_client=minkipc-tests +ubuntu:gp_test_client=minkipc-tests +ubuntu:qtee_supplicant=minkipc-qteesupplicant +ubuntu:sfs_config=minkipc-qteesupplicant + # --------------------------------------------------------------------------- # Qualcomm userspace-resource-manager package set. # diff --git a/Runner/suites/Kernel/Baseport/MinkIPC_GPTEEC_Validation/MinkIPC_GPTEEC_Validation.yaml b/Runner/suites/Kernel/Baseport/MinkIPC_GPTEEC_Validation/MinkIPC_GPTEEC_Validation.yaml new file mode 100644 index 000000000..0a24e6c21 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/MinkIPC_GPTEEC_Validation/MinkIPC_GPTEEC_Validation.yaml @@ -0,0 +1,19 @@ +metadata: + name: MinkIPC_GPTEEC_Validation + format: "Lava-Test Test Definition 1.0" + description: "Runs gp_test_client with explicitly pre-loaded GlobalPlatform test TAs against the MinkIPC/QCOMTEE path." + os: + - linux + scope: + - functional + +params: + CLIENT: "" + TA_PATH: "" + +run: + steps: + - REPO_PATH=$PWD + - cd Runner/suites/Kernel/Baseport/MinkIPC_GPTEEC_Validation + - ./run.sh --client "${CLIENT}" --ta-path "${TA_PATH}" || true + - $REPO_PATH/Runner/utils/send-to-lava.sh MinkIPC_GPTEEC_Validation.res diff --git a/Runner/suites/Kernel/Baseport/MinkIPC_GPTEEC_Validation/README.md b/Runner/suites/Kernel/Baseport/MinkIPC_GPTEEC_Validation/README.md new file mode 100644 index 000000000..0e36fa95b --- /dev/null +++ b/Runner/suites/Kernel/Baseport/MinkIPC_GPTEEC_Validation/README.md @@ -0,0 +1,46 @@ +Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +SPDX-License-Identifier: BSD-3-Clause + +# MinkIPC GP TEEC Validation + +Runs the public `gp_test_client` GlobalPlatform Client API tests with explicitly +pre-loaded Trusted Applications: + +```sh +gp_test_client -l /path/to/gp/tas +``` + +The supplied directory must contain: + +- `example_gpapp_ta32.mbn` +- `gpsample.mbn` +- `gpsample2.mbn` +- `gptest.mbn` +- `gptest2.mbn` + +This corrects the previous behavior that checked a custom TA path but then ran +`gp_test_client -a`, which ignored that path. + +## Run + +```sh +cd Runner +GP_TA_PATH=/data/gp-tas ./run-test.sh MinkIPC_GPTEEC_Validation +``` + +Direct options: + +```sh +./run.sh --ta-path /data/gp-tas +./run.sh --client /opt/minkipc/gp_test_client --ta-path /data/gp-tas +``` + +The suite auto-discovers complete TA sets under `/lib/qtee-tas`, +`/usr/lib/qtee-tas`, `/data`, and `/opt/minkipc-ta`. Missing test assets produce +`SKIP`, not a platform failure. + +On Debian and Ubuntu, the mapped MinkIPC runtime and test packages are +automatically installed from the Qualcomm `qli-staging` trixie repository. +The `minkipc-tests` package provides `gp_test_client`, but the five GP test TAs +are not included and must be provisioned separately. Yocto, CentOS, and other +images use their image-provided components. diff --git a/Runner/suites/Kernel/Baseport/MinkIPC_GPTEEC_Validation/run.sh b/Runner/suites/Kernel/Baseport/MinkIPC_GPTEEC_Validation/run.sh new file mode 100755 index 000000000..d41284877 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/MinkIPC_GPTEEC_Validation/run.sh @@ -0,0 +1,181 @@ +#!/bin/sh +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause + +# ---------- Repo env + helpers ---------- +SCRIPT_DIR="$( + cd "$(dirname "$0")" || exit 1 + 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 + +if [ -z "$INIT_ENV" ]; then + echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + exit 1 +fi + +# Only source once (idempotent) +# NOTE: We intentionally **do not export** any new vars. They stay local to this shell. +if [ -z "${__INIT_ENV_LOADED:-}" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" + __INIT_ENV_LOADED=1 +fi + +# shellcheck disable=SC1090 +. "$INIT_ENV" +# shellcheck disable=SC1091 +. "$TOOLS/functestlib.sh" +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/lib_pkg_provider.sh" + +TESTNAME="MinkIPC_GPTEEC_Validation" +RES_FILE="$SCRIPT_DIR/$TESTNAME.res" +CLIENT_PATH="${GP_TEST_CLIENT:-}" +GP_TA_PATH="${GP_TA_PATH:-}" + +usage() { + echo "Usage: $0 [--client PATH] [--ta-path DIRECTORY]" +} + +while [ "$#" -gt 0 ]; do + case "$1" in + --client) + if [ "$#" -lt 2 ]; then + log_fail "--client requires a path" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + fi + CLIENT_PATH="$2" + shift 2 + ;; + --client=*) + CLIENT_PATH=${1#--client=} + shift + ;; + --ta-path) + if [ "$#" -lt 2 ]; then + log_fail "--ta-path requires a directory" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + fi + GP_TA_PATH="$2" + shift 2 + ;; + --ta-path=*) + GP_TA_PATH=${1#--ta-path=} + shift + ;; + -h|--help) + usage + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 + ;; + *) + log_fail "Unknown argument: $1" + usage + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + ;; + esac +done + +if ! minkipc_prepare_test_packages; then + log_fail "$TESTNAME FAIL: MinkIPC package preparation failed" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 +fi + +if [ -z "$CLIENT_PATH" ]; then + CLIENT_PATH=$(command -v gp_test_client 2>/dev/null || true) +fi +if [ -z "$CLIENT_PATH" ]; then + for candidate in /usr/bin/gp_test_client /usr/sbin/gp_test_client \ + /usr/local/bin/gp_test_client /usr/lib/minkipc/gp_test_client \ + /usr/libexec/minkipc/gp_test_client; do + if [ -x "$candidate" ]; then + CLIENT_PATH="$candidate" + break + fi + done +fi + +if [ -z "$CLIENT_PATH" ] || [ ! -x "$CLIENT_PATH" ]; then + log_skip "$TESTNAME SKIP: gp_test_client is not installed" + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 +fi + +ta_path_complete() { + tpc_path="$1" + [ -d "$tpc_path" ] || return 1 + for tpc_ta in example_gpapp_ta32.mbn gpsample.mbn gpsample2.mbn gptest.mbn gptest2.mbn; do + [ -r "$tpc_path/$tpc_ta" ] || return 1 + done + return 0 +} + +if [ -n "$GP_TA_PATH" ] && ! ta_path_complete "$GP_TA_PATH"; then + log_fail "$TESTNAME FAIL: --ta-path does not contain all five required GP test TAs: $GP_TA_PATH" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 +fi + +if [ -z "$GP_TA_PATH" ]; then + for root in /lib/qtee-tas /usr/lib/qtee-tas /data /opt/minkipc-ta; do + [ -d "$root" ] || continue + candidate=$(find "$root" -type f -name gptest.mbn 2>/dev/null | sed -n '1p') + if [ -n "$candidate" ]; then + candidate=$(dirname "$candidate") + if ta_path_complete "$candidate"; then + GP_TA_PATH="$candidate" + break + fi + fi + done +fi + +if [ -z "$GP_TA_PATH" ]; then + log_skip "$TESTNAME SKIP: GP test TAs are not provisioned" + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 +fi + +if [ ! -e /dev/tee0 ] && [ ! -e /dev/qcomtee ] && [ ! -e /dev/smcinvoke ]; then + log_skip "$TESTNAME SKIP: no MinkIPC/QCOMTEE device node is present" + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 +fi + +OS_ID=$(pkg_detect_os_id 2>/dev/null || echo unknown) +RUN_LOG="$SCRIPT_DIR/gp_test_client.log" +rm -f "$RUN_LOG" "$RES_FILE" +log_info "Starting $TESTNAME on OS=$OS_ID" +log_info "Pre-loading provisioned TAs with: $CLIENT_PATH -l $GP_TA_PATH" + +"$CLIENT_PATH" -l "$GP_TA_PATH" > "$RUN_LOG" 2>&1 +CLIENT_RC=$? +head -n 100 "$RUN_LOG" 2>/dev/null | while IFS= read -r line; do + if [ -n "$line" ]; then + log_info "[gp_test_client] $line" + fi +done + +if [ "$CLIENT_RC" -eq 0 ]; then + log_pass "$TESTNAME PASS" + echo "$TESTNAME PASS" > "$RES_FILE" + exit 0 +fi + +log_fail "$TESTNAME FAIL: gp_test_client returned $CLIENT_RC (see $RUN_LOG)" +echo "$TESTNAME FAIL" > "$RES_FILE" +exit 1 diff --git a/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Callback_Validation/MinkIPC_SMCInvoke_Callback_Validation.yaml b/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Callback_Validation/MinkIPC_SMCInvoke_Callback_Validation.yaml new file mode 100644 index 000000000..283e54669 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Callback_Validation/MinkIPC_SMCInvoke_Callback_Validation.yaml @@ -0,0 +1,20 @@ +metadata: + name: MinkIPC_SMCInvoke_Callback_Validation + format: "Lava-Test Test Definition 1.0" + description: "Runs the MinkIPC callback object test with the optional tzecotestapp TA." + os: + - linux + scope: + - functional + +params: + CLIENT: "" + TA_DIR: "" + ITERATIONS: "1" + +run: + steps: + - REPO_PATH=$PWD + - cd Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Callback_Validation + - ./run.sh --client "${CLIENT}" --ta-dir "${TA_DIR}" --iterations "${ITERATIONS}" || true + - $REPO_PATH/Runner/utils/send-to-lava.sh MinkIPC_SMCInvoke_Callback_Validation.res diff --git a/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Callback_Validation/README.md b/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Callback_Validation/README.md new file mode 100644 index 000000000..7fac30d7c --- /dev/null +++ b/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Callback_Validation/README.md @@ -0,0 +1,39 @@ +Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +SPDX-License-Identifier: BSD-3-Clause + +# MinkIPC SMCInvoke Callback Validation + +Runs the public MinkIPC callback object test: + +```sh +smcinvoke_client -c +``` + +The directory must contain `tzecotestapp.mbn`. The suite searches +`/lib/qtee-tas`, `/usr/lib/qtee-tas`, `/data`, and `/opt/minkipc-ta` when no +directory is supplied. + +## Run + +```sh +cd Runner +./run-test.sh MinkIPC_SMCInvoke_Callback_Validation +``` + +Direct options: + +```sh +./run.sh --iterations 5 +./run.sh --ta-dir /lib/qtee-tas +./run.sh --client /opt/minkipc/smcinvoke_client +``` + +The suite requires a zero client exit status and the public client's +`TEST PASSED!` marker. It returns `SKIP` when the client, device node, or test +TA is not provisioned. + +On Debian and Ubuntu, the mapped MinkIPC runtime and test packages are +automatically installed from the Qualcomm `qli-staging` trixie repository. +Those packages provide `smcinvoke_client`, but they do not contain +`tzecotestapp.mbn`; that optional TA must still be provisioned separately. +Yocto, CentOS, and other images use their image-provided components. diff --git a/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Callback_Validation/run.sh b/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Callback_Validation/run.sh new file mode 100755 index 000000000..3c57a0498 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Callback_Validation/run.sh @@ -0,0 +1,203 @@ +#!/bin/sh +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause + +# ---------- Repo env + helpers ---------- +SCRIPT_DIR="$( + cd "$(dirname "$0")" || exit 1 + 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 + +if [ -z "$INIT_ENV" ]; then + echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + exit 1 +fi + +# Only source once (idempotent) +# NOTE: We intentionally **do not export** any new vars. They stay local to this shell. +if [ -z "${__INIT_ENV_LOADED:-}" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" + __INIT_ENV_LOADED=1 +fi + +# shellcheck disable=SC1090 +. "$INIT_ENV" +# shellcheck disable=SC1091 +. "$TOOLS/functestlib.sh" +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/lib_pkg_provider.sh" + +TESTNAME="MinkIPC_SMCInvoke_Callback_Validation" +RES_FILE="$SCRIPT_DIR/$TESTNAME.res" +CLIENT_PATH="${SMCINVOKE_CLIENT:-}" +TA_DIR="${SMCINVOKE_TA_DIR:-}" +ITERATIONS="${SMCINVOKE_ITERATIONS:-1}" + +usage() { + echo "Usage: $0 [--client PATH] [--ta-dir DIRECTORY] [--iterations N]" +} + +while [ "$#" -gt 0 ]; do + case "$1" in + --client) + if [ "$#" -lt 2 ]; then + log_fail "--client requires a path" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + fi + CLIENT_PATH="$2" + shift 2 + ;; + --client=*) + CLIENT_PATH=${1#--client=} + shift + ;; + --ta-dir) + if [ "$#" -lt 2 ]; then + log_fail "--ta-dir requires a directory" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + fi + TA_DIR="$2" + shift 2 + ;; + --ta-dir=*) + TA_DIR=${1#--ta-dir=} + shift + ;; + --iterations) + if [ "$#" -lt 2 ]; then + log_fail "--iterations requires a value" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + fi + ITERATIONS="$2" + shift 2 + ;; + --iterations=*) + ITERATIONS=${1#--iterations=} + shift + ;; + -h|--help) + usage + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 + ;; + *) + log_fail "Unknown argument: $1" + usage + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + ;; + esac +done + +case "$ITERATIONS" in + ''|*[!0-9]*|0) + log_fail "Invalid iteration count: $ITERATIONS" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + ;; +esac + +if ! minkipc_prepare_test_packages; then + log_fail "$TESTNAME FAIL: MinkIPC package preparation failed" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 +fi + +if [ -z "$CLIENT_PATH" ]; then + CLIENT_PATH=$(command -v smcinvoke_client 2>/dev/null || true) +fi + +if [ -z "$CLIENT_PATH" ]; then + for candidate in \ + /usr/bin/smcinvoke_client \ + /usr/sbin/smcinvoke_client \ + /usr/local/bin/smcinvoke_client \ + /usr/lib/minkipc/smcinvoke_client \ + /usr/libexec/minkipc/smcinvoke_client + do + if [ -x "$candidate" ]; then + CLIENT_PATH="$candidate" + break + fi + done +fi + +if [ -z "$CLIENT_PATH" ] || [ ! -x "$CLIENT_PATH" ]; then + log_skip "$TESTNAME SKIP: smcinvoke_client is not installed" + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 +fi + +if [ ! -e /dev/smcinvoke ] && [ ! -e /dev/tee0 ] && [ ! -e /dev/qcomtee ]; then + log_skip "$TESTNAME SKIP: no SMCInvoke/QCOMTEE device node is present" + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 +fi + +if [ -z "$TA_DIR" ]; then + for candidate in \ + /lib/qtee-tas \ + /usr/lib/qtee-tas \ + /data \ + /opt/minkipc-ta + do + if [ -r "$candidate/tzecotestapp.mbn" ]; then + TA_DIR="$candidate" + break + fi + done +fi + +if [ -z "$TA_DIR" ] || [ ! -r "$TA_DIR/tzecotestapp.mbn" ]; then + log_skip "$TESTNAME SKIP: tzecotestapp.mbn is not provisioned" + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 +fi + +OS_ID=$(pkg_detect_os_id 2>/dev/null || echo unknown) +RUN_LOG="$SCRIPT_DIR/smcinvoke_callback.log" +rm -f "$RUN_LOG" "$RES_FILE" + +log_info "Starting $TESTNAME on OS=$OS_ID" +log_info "Using callback test TA: $TA_DIR/tzecotestapp.mbn" +log_info "Running callback object validation: $CLIENT_PATH -c $TA_DIR $ITERATIONS" + +"$CLIENT_PATH" -c "$TA_DIR" "$ITERATIONS" > "$RUN_LOG" 2>&1 +CLIENT_RC=$? + +head -n 120 "$RUN_LOG" 2>/dev/null | +while IFS= read -r line; do + if [ -n "$line" ]; then + log_info "[smcinvoke_client] $line" + fi +done + +if [ "$CLIENT_RC" -eq 0 ] && grep -Fq "TEST PASSED!" "$RUN_LOG"; then + log_pass "$TESTNAME PASS" + echo "$TESTNAME PASS" > "$RES_FILE" + exit 0 +fi + +if [ "$CLIENT_RC" -eq 0 ]; then + log_fail "$TESTNAME FAIL: callback test did not report TEST PASSED (see $RUN_LOG)" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 +fi + +log_fail "$TESTNAME FAIL: callback test returned $CLIENT_RC (see $RUN_LOG)" +echo "$TESTNAME FAIL" > "$RES_FILE" +exit 1 diff --git a/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Internal_Validation/MinkIPC_SMCInvoke_Internal_Validation.yaml b/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Internal_Validation/MinkIPC_SMCInvoke_Internal_Validation.yaml new file mode 100644 index 000000000..b581d2f36 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Internal_Validation/MinkIPC_SMCInvoke_Internal_Validation.yaml @@ -0,0 +1,22 @@ +metadata: + name: MinkIPC_SMCInvoke_Internal_Validation + format: "Lava-Test Test Definition 1.0" + description: "Runs a safe MinkIPC internal filesystem command with the optional smplap TA." + os: + - linux + scope: + - functional + +params: + CLIENT: "" + TA_PATH: "" + COMMAND: "6" + ITERATIONS: "1" + TYPE: "0" + +run: + steps: + - REPO_PATH=$PWD + - cd Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Internal_Validation + - ./run.sh --client "${CLIENT}" --ta "${TA_PATH}" --command "${COMMAND}" --iterations "${ITERATIONS}" --type "${TYPE}" || true + - $REPO_PATH/Runner/utils/send-to-lava.sh MinkIPC_SMCInvoke_Internal_Validation.res diff --git a/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Internal_Validation/README.md b/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Internal_Validation/README.md new file mode 100644 index 000000000..ed36398c0 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Internal_Validation/README.md @@ -0,0 +1,44 @@ +Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +SPDX-License-Identifier: BSD-3-Clause + +# MinkIPC SMCInvoke Internal Validation + +Runs one internal MinkIPC filesystem test through the optional `smplap64.mbn` +TA: + +```sh +smcinvoke_client -i +``` + +The default command is `6`, the FS test. Command `5`, the GPFS test, is also +allowed. Commands `14`, `15`, and `17` are deliberately blocked because they +can provision, erase, or write RPMB state. + +Application type `0` selects the default 64-bit request and type `1` selects +the 32-bit request format supported by the public client. + +## Run + +```sh +cd Runner +./run-test.sh MinkIPC_SMCInvoke_Internal_Validation +``` + +Direct options: + +```sh +./run.sh --ta /data/smplap64.mbn +./run.sh --command 5 --iterations 5 +./run.sh --type 1 --ta /data/smplap64.mbn +``` + +The suite searches the standard QTEE TA directories when no TA path is +supplied. It requires both a zero client exit status and the exact +`TEST SUCCEEDED` marker because the public client can overwrite a command +failure return code during TA unload. Missing optional assets produce `SKIP`. + +On Debian and Ubuntu, the mapped MinkIPC runtime and test packages are +automatically installed from the Qualcomm `qli-staging` trixie repository. +Those packages provide `smcinvoke_client`, but they do not contain +`smplap64.mbn`; that optional TA must still be provisioned separately. Yocto, +CentOS, and other images use their image-provided components. diff --git a/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Internal_Validation/run.sh b/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Internal_Validation/run.sh new file mode 100755 index 000000000..f1ef8d6e0 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Internal_Validation/run.sh @@ -0,0 +1,272 @@ +#!/bin/sh +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause + +# ---------- Repo env + helpers ---------- +SCRIPT_DIR="$( + cd "$(dirname "$0")" || exit 1 + 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 + +if [ -z "$INIT_ENV" ]; then + echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + exit 1 +fi + +# Only source once (idempotent) +# NOTE: We intentionally **do not export** any new vars. They stay local to this shell. +if [ -z "${__INIT_ENV_LOADED:-}" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" + __INIT_ENV_LOADED=1 +fi + +# shellcheck disable=SC1090 +. "$INIT_ENV" +# shellcheck disable=SC1091 +. "$TOOLS/functestlib.sh" +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/lib_pkg_provider.sh" + +TESTNAME="MinkIPC_SMCInvoke_Internal_Validation" +RES_FILE="$SCRIPT_DIR/$TESTNAME.res" +CLIENT_PATH="${SMCINVOKE_CLIENT:-}" +TA_PATH="${SMCINVOKE_INTERNAL_TA:-}" +COMMAND_ID="${SMCINVOKE_INTERNAL_COMMAND:-6}" +ITERATIONS="${SMCINVOKE_ITERATIONS:-1}" +APP_TYPE="${SMCINVOKE_INTERNAL_TYPE:-0}" + +usage() { + echo "Usage: $0 [--client PATH] [--ta PATH] [--command 5|6] [--iterations N] [--type 0|1]" +} + +while [ "$#" -gt 0 ]; do + case "$1" in + --client) + if [ "$#" -lt 2 ]; then + log_fail "--client requires a path" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + fi + CLIENT_PATH="$2" + shift 2 + ;; + --client=*) + CLIENT_PATH=${1#--client=} + shift + ;; + --ta) + if [ "$#" -lt 2 ]; then + log_fail "--ta requires a path" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + fi + TA_PATH="$2" + shift 2 + ;; + --ta=*) + TA_PATH=${1#--ta=} + shift + ;; + --command) + if [ "$#" -lt 2 ]; then + log_fail "--command requires a value" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + fi + COMMAND_ID="$2" + shift 2 + ;; + --command=*) + COMMAND_ID=${1#--command=} + shift + ;; + --iterations) + if [ "$#" -lt 2 ]; then + log_fail "--iterations requires a value" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + fi + ITERATIONS="$2" + shift 2 + ;; + --iterations=*) + ITERATIONS=${1#--iterations=} + shift + ;; + --type) + if [ "$#" -lt 2 ]; then + log_fail "--type requires a value" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + fi + APP_TYPE="$2" + shift 2 + ;; + --type=*) + APP_TYPE=${1#--type=} + shift + ;; + -h|--help) + usage + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 + ;; + *) + log_fail "Unknown argument: $1" + usage + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + ;; + esac +done + +case "$ITERATIONS" in + ''|*[!0-9]*|0) + log_fail "Invalid iteration count: $ITERATIONS" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + ;; +esac + +case "$COMMAND_ID" in + 5|6) + ;; + 14|15|17) + log_fail "Unsafe internal command $COMMAND_ID is blocked because it can modify RPMB state" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + ;; + *) + log_fail "Unsupported internal command: $COMMAND_ID, allowed commands are 5 and 6" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + ;; +esac + +case "$APP_TYPE" in + 0|1) + ;; + *) + log_fail "Invalid application type: $APP_TYPE, allowed values are 0 and 1" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + ;; +esac + +if ! minkipc_prepare_test_packages; then + log_fail "$TESTNAME FAIL: MinkIPC package preparation failed" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 +fi + +if [ -z "$CLIENT_PATH" ]; then + CLIENT_PATH=$(command -v smcinvoke_client 2>/dev/null || true) +fi + +if [ -z "$CLIENT_PATH" ]; then + for candidate in \ + /usr/bin/smcinvoke_client \ + /usr/sbin/smcinvoke_client \ + /usr/local/bin/smcinvoke_client \ + /usr/lib/minkipc/smcinvoke_client \ + /usr/libexec/minkipc/smcinvoke_client + do + if [ -x "$candidate" ]; then + CLIENT_PATH="$candidate" + break + fi + done +fi + +if [ -z "$CLIENT_PATH" ] || [ ! -x "$CLIENT_PATH" ]; then + log_skip "$TESTNAME SKIP: smcinvoke_client is not installed" + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 +fi + +if [ ! -e /dev/smcinvoke ] && [ ! -e /dev/tee0 ] && [ ! -e /dev/qcomtee ]; then + log_skip "$TESTNAME SKIP: no SMCInvoke/QCOMTEE device node is present" + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 +fi + +if [ -z "$TA_PATH" ]; then + for candidate in \ + /lib/qtee-tas/smplap64.mbn \ + /usr/lib/qtee-tas/smplap64.mbn \ + /data/smplap64.mbn \ + /opt/minkipc-ta/smplap64.mbn + do + if [ -r "$candidate" ]; then + TA_PATH="$candidate" + break + fi + done +fi + +if [ -z "$TA_PATH" ] || [ ! -r "$TA_PATH" ]; then + log_skip "$TESTNAME SKIP: smplap64.mbn is not provisioned" + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 +fi + +OS_ID=$(pkg_detect_os_id 2>/dev/null || echo unknown) +RUN_LOG="$SCRIPT_DIR/smcinvoke_internal.log" +rm -f "$RUN_LOG" "$RES_FILE" + +case "$COMMAND_ID" in + 5) + COMMAND_NAME="GPFS" + ;; + 6) + COMMAND_NAME="FS" + ;; +esac + +log_info "Starting $TESTNAME on OS=$OS_ID" +log_info "Using internal test TA: $TA_PATH" +log_info "Running internal $COMMAND_NAME validation: $CLIENT_PATH -i $TA_PATH $COMMAND_ID $ITERATIONS $APP_TYPE" + +"$CLIENT_PATH" \ + -i \ + "$TA_PATH" \ + "$COMMAND_ID" \ + "$ITERATIONS" \ + "$APP_TYPE" > "$RUN_LOG" 2>&1 +CLIENT_RC=$? + +head -n 120 "$RUN_LOG" 2>/dev/null | +while IFS= read -r line; do + if [ -n "$line" ]; then + log_info "[smcinvoke_client] $line" + fi +done + +SUCCESS_MARKER="TEST SUCCEEDED for $ITERATIONS iterations" + +if [ "$CLIENT_RC" -eq 0 ] && grep -Fq "$SUCCESS_MARKER" "$RUN_LOG"; then + log_pass "$TESTNAME PASS" + echo "$TESTNAME PASS" > "$RES_FILE" + exit 0 +fi + +if [ "$CLIENT_RC" -eq 0 ]; then + log_fail "$TESTNAME FAIL: internal test did not report $SUCCESS_MARKER (see $RUN_LOG)" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 +fi + +log_fail "$TESTNAME FAIL: internal test returned $CLIENT_RC (see $RUN_LOG)" +echo "$TESTNAME FAIL" > "$RES_FILE" +exit 1 diff --git a/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Memory_Object_Validation/MinkIPC_SMCInvoke_Memory_Object_Validation.yaml b/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Memory_Object_Validation/MinkIPC_SMCInvoke_Memory_Object_Validation.yaml new file mode 100644 index 000000000..d92cd01a4 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Memory_Object_Validation/MinkIPC_SMCInvoke_Memory_Object_Validation.yaml @@ -0,0 +1,20 @@ +metadata: + name: MinkIPC_SMCInvoke_Memory_Object_Validation + format: "Lava-Test Test Definition 1.0" + description: "Runs the MinkIPC memory object test with the optional tzecotestapp TA." + os: + - linux + scope: + - functional + +params: + CLIENT: "" + TA_DIR: "" + ITERATIONS: "1" + +run: + steps: + - REPO_PATH=$PWD + - cd Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Memory_Object_Validation + - ./run.sh --client "${CLIENT}" --ta-dir "${TA_DIR}" --iterations "${ITERATIONS}" || true + - $REPO_PATH/Runner/utils/send-to-lava.sh MinkIPC_SMCInvoke_Memory_Object_Validation.res diff --git a/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Memory_Object_Validation/README.md b/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Memory_Object_Validation/README.md new file mode 100644 index 000000000..19abc0d24 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Memory_Object_Validation/README.md @@ -0,0 +1,39 @@ +Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +SPDX-License-Identifier: BSD-3-Clause + +# MinkIPC SMCInvoke Memory Object Validation + +Runs the public MinkIPC memory object test: + +```sh +smcinvoke_client -m +``` + +The directory must contain `tzecotestapp.mbn`. The suite searches +`/lib/qtee-tas`, `/usr/lib/qtee-tas`, `/data`, and `/opt/minkipc-ta` when no +directory is supplied. + +## Run + +```sh +cd Runner +./run-test.sh MinkIPC_SMCInvoke_Memory_Object_Validation +``` + +Direct options: + +```sh +./run.sh --iterations 5 +./run.sh --ta-dir /lib/qtee-tas +./run.sh --client /opt/minkipc/smcinvoke_client +``` + +The suite requires a zero client exit status and the public client's +`TEST PASSED!` marker. It returns `SKIP` when the client, device node, or test +TA is not provisioned. + +On Debian and Ubuntu, the mapped MinkIPC runtime and test packages are +automatically installed from the Qualcomm `qli-staging` trixie repository. +Those packages provide `smcinvoke_client`, but they do not contain +`tzecotestapp.mbn`; that optional TA must still be provisioned separately. +Yocto, CentOS, and other images use their image-provided components. diff --git a/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Memory_Object_Validation/run.sh b/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Memory_Object_Validation/run.sh new file mode 100755 index 000000000..0e5804d25 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Memory_Object_Validation/run.sh @@ -0,0 +1,203 @@ +#!/bin/sh +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause + +# ---------- Repo env + helpers ---------- +SCRIPT_DIR="$( + cd "$(dirname "$0")" || exit 1 + 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 + +if [ -z "$INIT_ENV" ]; then + echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + exit 1 +fi + +# Only source once (idempotent) +# NOTE: We intentionally **do not export** any new vars. They stay local to this shell. +if [ -z "${__INIT_ENV_LOADED:-}" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" + __INIT_ENV_LOADED=1 +fi + +# shellcheck disable=SC1090 +. "$INIT_ENV" +# shellcheck disable=SC1091 +. "$TOOLS/functestlib.sh" +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/lib_pkg_provider.sh" + +TESTNAME="MinkIPC_SMCInvoke_Memory_Object_Validation" +RES_FILE="$SCRIPT_DIR/$TESTNAME.res" +CLIENT_PATH="${SMCINVOKE_CLIENT:-}" +TA_DIR="${SMCINVOKE_TA_DIR:-}" +ITERATIONS="${SMCINVOKE_ITERATIONS:-1}" + +usage() { + echo "Usage: $0 [--client PATH] [--ta-dir DIRECTORY] [--iterations N]" +} + +while [ "$#" -gt 0 ]; do + case "$1" in + --client) + if [ "$#" -lt 2 ]; then + log_fail "--client requires a path" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + fi + CLIENT_PATH="$2" + shift 2 + ;; + --client=*) + CLIENT_PATH=${1#--client=} + shift + ;; + --ta-dir) + if [ "$#" -lt 2 ]; then + log_fail "--ta-dir requires a directory" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + fi + TA_DIR="$2" + shift 2 + ;; + --ta-dir=*) + TA_DIR=${1#--ta-dir=} + shift + ;; + --iterations) + if [ "$#" -lt 2 ]; then + log_fail "--iterations requires a value" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + fi + ITERATIONS="$2" + shift 2 + ;; + --iterations=*) + ITERATIONS=${1#--iterations=} + shift + ;; + -h|--help) + usage + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 + ;; + *) + log_fail "Unknown argument: $1" + usage + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + ;; + esac +done + +case "$ITERATIONS" in + ''|*[!0-9]*|0) + log_fail "Invalid iteration count: $ITERATIONS" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + ;; +esac + +if ! minkipc_prepare_test_packages; then + log_fail "$TESTNAME FAIL: MinkIPC package preparation failed" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 +fi + +if [ -z "$CLIENT_PATH" ]; then + CLIENT_PATH=$(command -v smcinvoke_client 2>/dev/null || true) +fi + +if [ -z "$CLIENT_PATH" ]; then + for candidate in \ + /usr/bin/smcinvoke_client \ + /usr/sbin/smcinvoke_client \ + /usr/local/bin/smcinvoke_client \ + /usr/lib/minkipc/smcinvoke_client \ + /usr/libexec/minkipc/smcinvoke_client + do + if [ -x "$candidate" ]; then + CLIENT_PATH="$candidate" + break + fi + done +fi + +if [ -z "$CLIENT_PATH" ] || [ ! -x "$CLIENT_PATH" ]; then + log_skip "$TESTNAME SKIP: smcinvoke_client is not installed" + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 +fi + +if [ ! -e /dev/smcinvoke ] && [ ! -e /dev/tee0 ] && [ ! -e /dev/qcomtee ]; then + log_skip "$TESTNAME SKIP: no SMCInvoke/QCOMTEE device node is present" + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 +fi + +if [ -z "$TA_DIR" ]; then + for candidate in \ + /lib/qtee-tas \ + /usr/lib/qtee-tas \ + /data \ + /opt/minkipc-ta + do + if [ -r "$candidate/tzecotestapp.mbn" ]; then + TA_DIR="$candidate" + break + fi + done +fi + +if [ -z "$TA_DIR" ] || [ ! -r "$TA_DIR/tzecotestapp.mbn" ]; then + log_skip "$TESTNAME SKIP: tzecotestapp.mbn is not provisioned" + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 +fi + +OS_ID=$(pkg_detect_os_id 2>/dev/null || echo unknown) +RUN_LOG="$SCRIPT_DIR/smcinvoke_memory_object.log" +rm -f "$RUN_LOG" "$RES_FILE" + +log_info "Starting $TESTNAME on OS=$OS_ID" +log_info "Using memory object test TA: $TA_DIR/tzecotestapp.mbn" +log_info "Running memory object validation: $CLIENT_PATH -m $TA_DIR $ITERATIONS" + +"$CLIENT_PATH" -m "$TA_DIR" "$ITERATIONS" > "$RUN_LOG" 2>&1 +CLIENT_RC=$? + +head -n 120 "$RUN_LOG" 2>/dev/null | +while IFS= read -r line; do + if [ -n "$line" ]; then + log_info "[smcinvoke_client] $line" + fi +done + +if [ "$CLIENT_RC" -eq 0 ] && grep -Fq "TEST PASSED!" "$RUN_LOG"; then + log_pass "$TESTNAME PASS" + echo "$TESTNAME PASS" > "$RES_FILE" + exit 0 +fi + +if [ "$CLIENT_RC" -eq 0 ]; then + log_fail "$TESTNAME FAIL: memory object test did not report TEST PASSED (see $RUN_LOG)" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 +fi + +log_fail "$TESTNAME FAIL: memory object test returned $CLIENT_RC (see $RUN_LOG)" +echo "$TESTNAME FAIL" > "$RES_FILE" +exit 1 diff --git a/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Validation/MinkIPC_SMCInvoke_Validation.yaml b/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Validation/MinkIPC_SMCInvoke_Validation.yaml new file mode 100644 index 000000000..39d0a246d --- /dev/null +++ b/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Validation/MinkIPC_SMCInvoke_Validation.yaml @@ -0,0 +1,19 @@ +metadata: + name: MinkIPC_SMCInvoke_Validation + format: "Lava-Test Test Definition 1.0" + description: "Runs the MinkIPC smcinvoke_client QTEE diagnostic as a focused, asset-optional test." + os: + - linux + scope: + - functional + +params: + CLIENT: "" + ITERATIONS: "1" + +run: + steps: + - REPO_PATH=$PWD + - cd Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Validation + - ./run.sh --client "${CLIENT}" --iterations "${ITERATIONS}" || true + - $REPO_PATH/Runner/utils/send-to-lava.sh MinkIPC_SMCInvoke_Validation.res diff --git a/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Validation/README.md b/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Validation/README.md new file mode 100644 index 000000000..a40c70e63 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Validation/README.md @@ -0,0 +1,32 @@ +Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +SPDX-License-Identifier: BSD-3-Clause + +# MinkIPC SMCInvoke Validation + +Runs the public MinkIPC QTEE diagnostic command: + +```sh +smcinvoke_client -d 1 +``` + +The test is separate from `MinkIPC_Validation` because the diagnostic +client may be delivered only in test-enabled images. + +## Run + +```sh +cd Runner +./run-test.sh MinkIPC_SMCInvoke_Validation +``` + +Direct options: + +```sh +./run.sh --iterations 5 +./run.sh --client /opt/minkipc/smcinvoke_client +``` + +On Debian and Ubuntu, the suite automatically ensures the mapped MinkIPC +runtime and test packages from the Qualcomm `qli-staging` trixie repository. +Yocto, CentOS, and other images continue using image-provided components. The +suite returns `SKIP` when the client or a supported device node remains absent. diff --git a/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Validation/run.sh b/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Validation/run.sh new file mode 100755 index 000000000..c0afeb49c --- /dev/null +++ b/Runner/suites/Kernel/Baseport/MinkIPC_SMCInvoke_Validation/run.sh @@ -0,0 +1,160 @@ +#!/bin/sh +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause + +# ---------- Repo env + helpers ---------- +SCRIPT_DIR="$( + cd "$(dirname "$0")" || exit 1 + 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 + +if [ -z "$INIT_ENV" ]; then + echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + exit 1 +fi + +# Only source once (idempotent) +# NOTE: We intentionally **do not export** any new vars. They stay local to this shell. +if [ -z "${__INIT_ENV_LOADED:-}" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" + __INIT_ENV_LOADED=1 +fi + +# shellcheck disable=SC1090 +. "$INIT_ENV" +# shellcheck disable=SC1091 +. "$TOOLS/functestlib.sh" +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/lib_pkg_provider.sh" + +TESTNAME="MinkIPC_SMCInvoke_Validation" +RES_FILE="$SCRIPT_DIR/$TESTNAME.res" +CLIENT_PATH="${SMCINVOKE_CLIENT:-}" +ITERATIONS=1 + +usage() { + echo "Usage: $0 [--client PATH] [--iterations N]" +} + +while [ "$#" -gt 0 ]; do + case "$1" in + --client) + if [ "$#" -lt 2 ]; then + log_fail "--client requires a path" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + fi + CLIENT_PATH="$2" + shift 2 + ;; + --client=*) + CLIENT_PATH=${1#--client=} + shift + ;; + --iterations) + if [ "$#" -lt 2 ]; then + log_fail "--iterations requires a value" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + fi + ITERATIONS="$2" + shift 2 + ;; + --iterations=*) + ITERATIONS=${1#--iterations=} + shift + ;; + -h|--help) + usage + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 + ;; + *) + log_fail "Unknown argument: $1" + usage + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + ;; + esac +done + +case "$ITERATIONS" in + ''|*[!0-9]*|0) + log_fail "Invalid iteration count: $ITERATIONS" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + ;; +esac + +if ! minkipc_prepare_test_packages; then + log_fail "$TESTNAME FAIL: MinkIPC package preparation failed" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 +fi + +if [ -z "$CLIENT_PATH" ]; then + CLIENT_PATH=$(command -v smcinvoke_client 2>/dev/null || true) +fi +if [ -z "$CLIENT_PATH" ]; then + for candidate in /usr/bin/smcinvoke_client /usr/sbin/smcinvoke_client \ + /usr/local/bin/smcinvoke_client /usr/lib/minkipc/smcinvoke_client \ + /usr/libexec/minkipc/smcinvoke_client; do + if [ -x "$candidate" ]; then + CLIENT_PATH="$candidate" + break + fi + done +fi + +if [ -z "$CLIENT_PATH" ] || [ ! -x "$CLIENT_PATH" ]; then + log_skip "$TESTNAME SKIP: smcinvoke_client is not installed" + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 +fi + +if [ ! -e /dev/smcinvoke ] && [ ! -e /dev/tee0 ] && [ ! -e /dev/qcomtee ]; then + log_skip "$TESTNAME SKIP: no SMCInvoke/QCOMTEE device node is present" + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 +fi + +OS_ID=$(pkg_detect_os_id 2>/dev/null || echo unknown) +RUN_LOG="$SCRIPT_DIR/smcinvoke_client.log" +rm -f "$RUN_LOG" "$RES_FILE" +log_info "Starting $TESTNAME on OS=$OS_ID" +log_info "Running public MinkIPC diagnostic: $CLIENT_PATH -d $ITERATIONS" + +"$CLIENT_PATH" -d "$ITERATIONS" > "$RUN_LOG" 2>&1 +CLIENT_RC=$? +head -n 80 "$RUN_LOG" 2>/dev/null | while IFS= read -r line; do + if [ -n "$line" ]; then + log_info "[smcinvoke_client] $line" + fi +done + +if [ "$CLIENT_RC" -eq 0 ] && grep -Fq "TEST PASSED!" "$RUN_LOG"; then + log_pass "$TESTNAME PASS" + echo "$TESTNAME PASS" > "$RES_FILE" + exit 0 +fi + +if [ "$CLIENT_RC" -eq 0 ]; then + log_fail "$TESTNAME FAIL: smcinvoke_client did not report TEST PASSED (see $RUN_LOG)" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 +fi + +log_fail "$TESTNAME FAIL: smcinvoke_client returned $CLIENT_RC (see $RUN_LOG)" +echo "$TESTNAME FAIL" > "$RES_FILE" +exit 1 diff --git a/Runner/suites/Kernel/Baseport/MinkIPC_Validation/MinkIPC_Validation.yaml b/Runner/suites/Kernel/Baseport/MinkIPC_Validation/MinkIPC_Validation.yaml new file mode 100644 index 000000000..86e7bc7d8 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/MinkIPC_Validation/MinkIPC_Validation.yaml @@ -0,0 +1,15 @@ +metadata: + name: MinkIPC_Validation + format: "Lava-Test Test Definition 1.0" + description: "Qualcomm MinkIPC health validation for legacy SMCInvoke and modern QCOMTEE-backed TEE deployments across Yocto, Debian, Ubuntu, and CentOS." + os: + - linux + scope: + - functional + +run: + steps: + - REPO_PATH=$PWD + - cd Runner/suites/Kernel/Baseport/MinkIPC_Validation + - ./run.sh || true + - $REPO_PATH/Runner/utils/send-to-lava.sh MinkIPC_Validation.res diff --git a/Runner/suites/Kernel/Baseport/MinkIPC_Validation/README.md b/Runner/suites/Kernel/Baseport/MinkIPC_Validation/README.md new file mode 100644 index 000000000..006f11ba2 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/MinkIPC_Validation/README.md @@ -0,0 +1,64 @@ +Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +SPDX-License-Identifier: BSD-3-Clause + +# MinkIPC Validation + +Health validation for the Qualcomm MinkIPC userspace-to-QTEE path. +The checks follow the public [qualcomm/minkipc](https://github.com/qualcomm/minkipc) +implementation while retaining compatibility with the legacy +`/dev/smcinvoke` interface. + +This suite does not run asset-dependent diagnostics. Those are separate tests: + +- `MinkIPC_SMCInvoke_Validation` +- `MinkIPC_SMCInvoke_Callback_Validation` +- `MinkIPC_SMCInvoke_Memory_Object_Validation` +- `MinkIPC_SMCInvoke_Internal_Validation` +- `MinkIPC_GPTEEC_Validation` + +## Applicability + +The suite returns `SKIP` when no Qualcomm SMCInvoke/QCOMTEE hardware signal is +present. A generic `/dev/tee*` node is accepted only when QCOMTEE backing can be +identified through sysfs or the installed MinkIPC runtime and +`qtee_supplicant` service. + +The public `meta-qcom` build links the static `qcomtee` library privately into +`libminkadaptor`, so a separate runtime `libqcomtee.so` is not required. + +## Test cases + +| ID | Validation | +|---|---| +| TC-01 | Legacy `/dev/smcinvoke` or QCOMTEE-backed TEE character device | +| TC-02 | Architecture-neutral `libminkadaptor` and optional `libminkteec` discovery | +| TC-03 | `qtee_supplicant` process and systemd/SysV service health | +| TC-04 | `sfsconfig.service` result, persistent-mount ordering, and SFS directories | +| TC-05 | Device-node read/write accessibility | +| TC-06 | Relevant kernel-log error scan with retained snapshot and filtered error logs | + +## Distribution support + +- Yocto: validates image-provided `minkipc` and `minkipc-qteesupplicant` components. +- Debian/Ubuntu: automatically ensures `libminkadaptor0`, `libminkteec1`, + `minkipc-qteesupplicant`, and `minkipc-tests` from the Qualcomm + `qli-staging` trixie repository before validation. +- CentOS/RHEL family: supports `/lib64`/`/usr/lib64`, RPM images, systemd, and SysV fallback. + +Only Debian and Ubuntu use package recovery. Yocto, CentOS, and other images +continue using their image-provided components. + +## Run + +```sh +cd Runner +./run-test.sh MinkIPC_Validation +``` + +The result file contains one of: + +```text +MinkIPC_Validation PASS +MinkIPC_Validation FAIL +MinkIPC_Validation SKIP +``` diff --git a/Runner/suites/Kernel/Baseport/MinkIPC_Validation/run.sh b/Runner/suites/Kernel/Baseport/MinkIPC_Validation/run.sh new file mode 100755 index 000000000..718b0f467 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/MinkIPC_Validation/run.sh @@ -0,0 +1,417 @@ +#!/bin/sh +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause + +# ---------- Repo env + helpers ---------- +SCRIPT_DIR="$( + cd "$(dirname "$0")" || exit 1 + 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 + +if [ -z "$INIT_ENV" ]; then + echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + exit 1 +fi + +# Only source once (idempotent) +# NOTE: We intentionally **do not export** any new vars. They stay local to this shell. +if [ -z "${__INIT_ENV_LOADED:-}" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" + __INIT_ENV_LOADED=1 +fi + +# shellcheck disable=SC1090 +. "$INIT_ENV" +# shellcheck disable=SC1091 +. "$TOOLS/functestlib.sh" +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/lib_pkg_provider.sh" + +TESTNAME="MinkIPC_Validation" +RES_FILE="$SCRIPT_DIR/$TESTNAME.res" + +test_path=$(find_test_case_by_name "$TESTNAME" 2>/dev/null || true) +if [ -n "$test_path" ]; then + cd "$test_path" || exit 1 +else + cd "$SCRIPT_DIR" || exit 1 +fi +rm -f "$RES_FILE" + +if ! minkipc_prepare_test_packages; then + log_fail "$TESTNAME FAIL: MinkIPC package preparation failed" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 +fi + +PASS_COUNT=0 +FAIL_COUNT=0 +SKIP_COUNT=0 + +pass_case() { + log_pass "$1" + PASS_COUNT=$((PASS_COUNT + 1)) +} + +fail_case() { + log_fail "$1" + FAIL_COUNT=$((FAIL_COUNT + 1)) +} + +skip_case() { + log_skip "$1" + SKIP_COUNT=$((SKIP_COUNT + 1)) +} + +process_running_quiet() { + get_pid "$1" >/dev/null 2>&1 +} + +module_loaded_quiet() { + is_module_loaded "$1" >/dev/null 2>&1 +} + +find_shared_library() { + fsl_name="$1" + fsl_path="" + + if command -v ldconfig >/dev/null 2>&1; then + fsl_path=$(ldconfig -p 2>/dev/null | awk -v name="$fsl_name" '$1 ~ ("^" name "\\.so") { print $NF; exit }') + if [ -n "$fsl_path" ] && [ -e "$fsl_path" ]; then + printf '%s\n' "$fsl_path" + return 0 + fi + fi + + for fsl_path in \ + /lib/"$fsl_name".so* /usr/lib/"$fsl_name".so* \ + /lib64/"$fsl_name".so* /usr/lib64/"$fsl_name".so* \ + /lib/*-linux-gnu/"$fsl_name".so* \ + /usr/lib/*-linux-gnu/"$fsl_name".so*; do + if [ -e "$fsl_path" ]; then + printf '%s\n' "$fsl_path" + return 0 + fi + done + + return 1 +} + +find_binary() { + fb_name="$1" + fb_path=$(command -v "$fb_name" 2>/dev/null || true) + if [ -n "$fb_path" ] && [ -x "$fb_path" ]; then + printf '%s\n' "$fb_path" + return 0 + fi + + for fb_path in /usr/bin/"$fb_name" /usr/sbin/"$fb_name" \ + /usr/local/bin/"$fb_name" /sbin/"$fb_name" /bin/"$fb_name" \ + /usr/libexec/"$fb_name" /usr/libexec/minkipc/"$fb_name"; do + if [ -x "$fb_path" ]; then + printf '%s\n' "$fb_path" + return 0 + fi + done + + return 1 +} + +path_is_mounted() { + pim_path="$1" + if command -v findmnt >/dev/null 2>&1; then + findmnt -n "$pim_path" >/dev/null 2>&1 + return $? + fi + awk -v path="$pim_path" '$2 == path { found=1 } END { exit !found }' /proc/self/mounts 2>/dev/null +} + +MINK_LIB=$(find_shared_library libminkadaptor 2>/dev/null || true) +MINKTEEC_LIB=$(find_shared_library libminkteec 2>/dev/null || true) +LEGACY_DEV="" +MODERN_DEV="" +MODERN_EVIDENCE="" + +if [ -e /dev/smcinvoke ]; then + LEGACY_DEV=/dev/smcinvoke +fi + +for candidate in /dev/qcomtee /dev/qcomtee0; do + if [ -e "$candidate" ]; then + MODERN_DEV="$candidate" + MODERN_EVIDENCE="QCOMTEE-specific device node" + break + fi +done + +if [ -z "$MODERN_DEV" ]; then + for tee_sys in /sys/class/tee/tee[0-9]*; do + [ -e "$tee_sys" ] || continue + tee_name=$(basename "$tee_sys") + tee_metadata=$( + cat "$tee_sys/name" "$tee_sys/uevent" "$tee_sys/device/uevent" 2>/dev/null + readlink -f "$tee_sys/device/driver" 2>/dev/null + ) + if printf '%s\n' "$tee_metadata" | grep -qiE 'qcom|qualcomm|qtee'; then + if [ -e "/dev/$tee_name" ]; then + MODERN_DEV="/dev/$tee_name" + MODERN_EVIDENCE="QCOMTEE sysfs metadata" + break + fi + fi + done +fi + +if [ -z "$MODERN_DEV" ] && [ -e /dev/tee0 ] && [ -n "$MINK_LIB" ]; then + if process_running_quiet qtee_supplicant || systemd_service_exists qteesupplicant; then + MODERN_DEV=/dev/tee0 + MODERN_EVIDENCE="libminkadaptor and qteesupplicant" + fi +fi + +LEGACY_MODULE=0 +MODERN_MODULE=0 +for module in qcom_smcinvoke smcinvoke; do + if module_loaded_quiet "$module"; then + LEGACY_MODULE=1 + break + fi +done +for module in qcomtee qcom_tee; do + if module_loaded_quiet "$module"; then + MODERN_MODULE=1 + break + fi +done + +MINKIPC_DMESG_DIR="$SCRIPT_DIR/minkipc_dmesg" +MINKIPC_DMESG_MODULES='qcomtee|qcom_tee|qcom-tee|qcom_smcinvoke|qcom-smcinvoke|smcinvoke|qtee' +MINKIPC_DMESG_EXCLUDE='optional|not supported|deferred probe' +MINKIPC_DMESG_ERRORS_FOUND=0 +MINKIPC_DMESG_READABLE=0 + +if scan_dmesg_errors \ + "$MINKIPC_DMESG_DIR" \ + "$MINKIPC_DMESG_MODULES" \ + "$MINKIPC_DMESG_EXCLUDE"; then + MINKIPC_DMESG_ERRORS_FOUND=1 +fi +if [ -s "$MINKIPC_DMESG_DIR/dmesg_snapshot.log" ]; then + MINKIPC_DMESG_READABLE=1 +fi + +DMESG_MARKER=0 +if [ "$MINKIPC_DMESG_READABLE" -eq 1 ] && \ + grep -qiE 'qcomtee|qcom[_ -]?smcinvoke|smcinvoke' \ + "$MINKIPC_DMESG_DIR/dmesg_snapshot.log"; then + DMESG_MARKER=1 +fi + +DT_MARKER=0 +if dt_confirm_node_or_compatible_all "qcom,smcinvoke" "qcom,qtee" >/dev/null 2>&1; then + DT_MARKER=1 +fi + +if [ -z "$MODERN_DEV" ] && [ -e /dev/tee0 ] && \ + { [ "$DMESG_MARKER" -eq 1 ] || [ "$DT_MARKER" -eq 1 ]; }; then + MODERN_DEV=/dev/tee0 + MODERN_EVIDENCE="Qualcomm kernel/device-tree evidence" +fi + +if [ -z "$LEGACY_DEV" ] && [ -z "$MODERN_DEV" ] && \ + [ "$LEGACY_MODULE" -eq 0 ] && [ "$MODERN_MODULE" -eq 0 ] && \ + [ "$DMESG_MARKER" -eq 0 ] && [ "$DT_MARKER" -eq 0 ]; then + log_skip "$TESTNAME SKIP: no Qualcomm MinkIPC/QCOMTEE hardware interface detected" + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 +fi + +OS_ID=$(pkg_detect_os_id 2>/dev/null || printf '%s\n' unknown) +detect_platform >/dev/null 2>&1 || true + +log_info "--------------------------------------------------------------------------" +log_info "Starting $TESTNAME" +log_info "OS=$OS_ID platform=${PLATFORM_TARGET:-unknown} arch=$(uname -m 2>/dev/null || echo unknown)" +log_info "Debian and Ubuntu use the verified MinkIPC package set, other distributions use image-provided components" + +log_info "--- TC-01: Qualcomm TEE driver interface ---" +if [ -n "$LEGACY_DEV" ]; then + if [ -c "$LEGACY_DEV" ]; then + pass_case "TC-01: legacy SMCInvoke character device found: $LEGACY_DEV" + else + fail_case "TC-01: legacy SMCInvoke path is not a character device: $LEGACY_DEV" + fi +elif [ "$LEGACY_MODULE" -eq 1 ]; then + fail_case "TC-01: legacy SMCInvoke module is loaded but /dev/smcinvoke is absent" +fi + +if [ -n "$MODERN_DEV" ]; then + if [ -c "$MODERN_DEV" ]; then + pass_case "TC-01: QCOMTEE-backed device found: $MODERN_DEV ($MODERN_EVIDENCE)" + else + fail_case "TC-01: QCOMTEE path is not a character device: $MODERN_DEV" + fi +elif [ "$MODERN_MODULE" -eq 1 ]; then + fail_case "TC-01: QCOMTEE module is loaded but no QCOMTEE-backed TEE device was identified" +fi + +if [ -z "$LEGACY_DEV" ] && [ -z "$MODERN_DEV" ] && \ + [ "$LEGACY_MODULE" -eq 0 ] && [ "$MODERN_MODULE" -eq 0 ]; then + fail_case "TC-01: Qualcomm MinkIPC hardware evidence exists but no usable device interface was found" +fi + +log_info "--- TC-02: architecture-neutral library discovery ---" +if [ -n "$MINK_LIB" ]; then + pass_case "TC-02: libminkadaptor found: $MINK_LIB" +else + fail_case "TC-02: libminkadaptor was not found through ldconfig or standard multiarch paths" +fi + +if [ -n "$MINKTEEC_LIB" ]; then + pass_case "TC-02: optional libminkteec found: $MINKTEEC_LIB" +else + skip_case "TC-02: libminkteec is not installed, GP diagnostics are covered by MinkIPC_GPTEEC_Validation" +fi + +log_info "--- TC-03: qtee_supplicant runtime health ---" +SUPPLICANT_BIN=$(find_binary qtee_supplicant 2>/dev/null || true) +SUPPLICANT_UNIT="" +for unit in qteesupplicant qtee-supplicant minkipc-qteesupplicant; do + if systemd_service_exists "$unit"; then + SUPPLICANT_UNIT="$unit" + break + fi +done + +SUPPLICANT_RUNNING=0 +for process in qtee_supplicant qteesupplicant; do + if process_running_quiet "$process"; then + SUPPLICANT_RUNNING=1 + break + fi +done + +if [ -n "$SUPPLICANT_UNIT" ]; then + if systemd_service_is_active "$SUPPLICANT_UNIT" || [ "$SUPPLICANT_RUNNING" -eq 1 ]; then + pass_case "TC-03: qtee_supplicant is active ($SUPPLICANT_UNIT)" + else + fail_case "TC-03: $SUPPLICANT_UNIT exists but qtee_supplicant is not running" + fi +elif [ "$SUPPLICANT_RUNNING" -eq 1 ]; then + pass_case "TC-03: qtee_supplicant process is running without a systemd unit" +else + SYSV_FOUND=0 + for init_script in /etc/init.d/qtee_supplicant /etc/init.d/qteesupplicant; do + if [ -x "$init_script" ]; then + SYSV_FOUND=1 + if "$init_script" status >/dev/null 2>&1; then + pass_case "TC-03: SysV qtee_supplicant service is running: $init_script" + else + fail_case "TC-03: SysV qtee_supplicant service is not running: $init_script" + fi + break + fi + done + if [ "$SYSV_FOUND" -eq 0 ]; then + if [ -n "$SUPPLICANT_BIN" ]; then + fail_case "TC-03: qtee_supplicant binary exists but no running service/process was found: $SUPPLICANT_BIN" + else + skip_case "TC-03: qtee_supplicant is not provisioned on this platform" + fi + fi +fi + +log_info "--- TC-04: secure-filesystem initialization and ordering ---" +SFS_UNIT="" +for unit in sfsconfig minkipc-sfsconfig sfs-config; do + if systemd_service_exists "$unit"; then + SFS_UNIT="$unit" + break + fi +done +SFS_BIN=$(find_binary sfs_config 2>/dev/null || find_binary sfsconfig 2>/dev/null || true) + +if [ -n "$SFS_UNIT" ]; then + SFS_ACTIVE=$(systemctl show "$SFS_UNIT" -p ActiveState --value 2>/dev/null || true) + SFS_RESULT=$(systemctl show "$SFS_UNIT" -p Result --value 2>/dev/null || true) + SFS_STATUS=$(systemctl show "$SFS_UNIT" -p ExecMainStatus --value 2>/dev/null || true) + if [ "$SFS_ACTIVE" = "active" ] || { [ "$SFS_RESULT" = "success" ] && [ "${SFS_STATUS:-0}" = "0" ]; }; then + pass_case "TC-04: one-shot SFS configuration completed successfully: $SFS_UNIT" + else + fail_case "TC-04: SFS configuration did not complete successfully: active=$SFS_ACTIVE result=$SFS_RESULT status=$SFS_STATUS" + fi + + if path_is_mounted /var/lib/tee; then + SFS_AFTER=$(systemctl show "$SFS_UNIT" -p After --value 2>/dev/null || true) + SFS_UNIT_TEXT=$(systemctl cat "$SFS_UNIT" 2>/dev/null || true) + if printf '%s\n%s\n' "$SFS_AFTER" "$SFS_UNIT_TEXT" | grep -qiE 'var-lib-tee\.mount|persist[^ ]*\.mount'; then + pass_case "TC-04: SFS service is ordered after persistent TEE storage" + else + fail_case "TC-04: /var/lib/tee is mounted but $SFS_UNIT has no persistent-mount ordering" + fi + else + skip_case "TC-04: /var/lib/tee is not a separate mounted persistence path, so mount ordering is not applicable" + fi + + for sfs_dir in /var/lib/tee/qtee_supplicant /var/lib/qtee_supplicant/vendor/tzstorage; do + if [ -d "$sfs_dir" ]; then + pass_case "TC-04: secure-filesystem directory exists: $sfs_dir" + else + fail_case "TC-04: expected secure-filesystem directory is missing: $sfs_dir" + fi + done +elif [ -n "$SFS_BIN" ]; then + skip_case "TC-04: SFS helper exists without systemd, so ordering cannot be verified portably: $SFS_BIN" +else + skip_case "TC-04: SFS configuration is not provisioned on this platform" +fi + +log_info "--- TC-05: device accessibility ---" +ACCESS_DEV=${MODERN_DEV:-$LEGACY_DEV} +if [ -n "$ACCESS_DEV" ]; then + if [ -r "$ACCESS_DEV" ] && [ -w "$ACCESS_DEV" ]; then + pass_case "TC-05: current test user can read and write $ACCESS_DEV" + else + fail_case "TC-05: current test user cannot read and write $ACCESS_DEV" + fi +else + skip_case "TC-05: no device node available for permission validation" +fi + +log_info "--- TC-06: kernel log health ---" +if [ "$MINKIPC_DMESG_READABLE" -eq 1 ]; then + if [ "$MINKIPC_DMESG_ERRORS_FOUND" -eq 1 ]; then + fail_case "TC-06: MinkIPC/QCOMTEE errors found in dmesg, see minkipc_dmesg/dmesg_errors.log" + else + pass_case "TC-06: no relevant MinkIPC/QCOMTEE errors found in dmesg" + fi +else + skip_case "TC-06: dmesg is not readable by the current test user" +fi + +log_info "Summary: pass=$PASS_COUNT fail=$FAIL_COUNT skip=$SKIP_COUNT" +if [ "$FAIL_COUNT" -gt 0 ]; then + log_fail "$TESTNAME FAIL" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 +fi +if [ "$PASS_COUNT" -gt 0 ]; then + log_pass "$TESTNAME PASS" + echo "$TESTNAME PASS" > "$RES_FILE" + exit 0 +fi + +log_skip "$TESTNAME SKIP" +echo "$TESTNAME SKIP" > "$RES_FILE" +exit 0 diff --git a/Runner/suites/Kernel/Baseport/Storage/RMTFS_Partition_Read_Validation/README.md b/Runner/suites/Kernel/Baseport/Storage/RMTFS_Partition_Read_Validation/README.md new file mode 100644 index 000000000..079c71835 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Storage/RMTFS_Partition_Read_Validation/README.md @@ -0,0 +1,32 @@ +Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +SPDX-License-Identifier: BSD-3-Clause + +# RMTFS Partition Read Validation + +Performs isolated, read-only checks of RMTFS partition labels. Search order is: + +1. `/dev/disk/by-partlabel` +2. `/dev/disk/by-name` +3. `/dev/block/by-name` +4. platform-specific Android-style `by-name` directories + +The default labels are `modemst1 modemst2 fsc fsg`. Each discovered block +device is read for one 512-byte sector with output discarded. Partition data is +never printed or written, and the test does not attempt to decide whether the +sector is all-zero. + +```sh +cd Runner +./run-test.sh RMTFS_Partition_Read_Validation +``` + +Direct customization: + +```sh +./run.sh --partitions "modemst1 modemst2 study tunning" +``` + +The suite prepares the same reversible module and service lifecycle used by +the other RMTFS tests. If the running daemon uses `-P`, missing requested +partition labels are a failure. Missing labels produce `SKIP` only for a +directory-backed daemon or a deployment where partition mode is not used. diff --git a/Runner/suites/Kernel/Baseport/Storage/RMTFS_Partition_Read_Validation/RMTFS_Partition_Read_Validation.yaml b/Runner/suites/Kernel/Baseport/Storage/RMTFS_Partition_Read_Validation/RMTFS_Partition_Read_Validation.yaml new file mode 100644 index 000000000..c733058e6 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Storage/RMTFS_Partition_Read_Validation/RMTFS_Partition_Read_Validation.yaml @@ -0,0 +1,18 @@ +metadata: + name: RMTFS_Partition_Read_Validation + format: "Lava-Test Test Definition 1.0" + description: "Performs reversible RMTFS setup and isolated one-sector read-only checks of partition labels." + os: + - linux + scope: + - functional + +params: + PARTITIONS: "modemst1 modemst2 fsc fsg" + +run: + steps: + - REPO_PATH=$PWD + - cd Runner/suites/Kernel/Baseport/Storage/RMTFS_Partition_Read_Validation + - ./run.sh --partitions "${PARTITIONS}" || true + - $REPO_PATH/Runner/utils/send-to-lava.sh RMTFS_Partition_Read_Validation.res diff --git a/Runner/suites/Kernel/Baseport/Storage/RMTFS_Partition_Read_Validation/run.sh b/Runner/suites/Kernel/Baseport/Storage/RMTFS_Partition_Read_Validation/run.sh new file mode 100755 index 000000000..c63e7c760 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Storage/RMTFS_Partition_Read_Validation/run.sh @@ -0,0 +1,212 @@ +#!/bin/sh +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause + +# ---------- Repo env + helpers ---------- +SCRIPT_DIR="$( + cd "$(dirname "$0")" || exit 1 + 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 + +if [ -z "$INIT_ENV" ]; then + echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + exit 1 +fi + +# Only source once (idempotent) +# NOTE: We intentionally **do not export** any new vars. They stay local to this shell. +if [ -z "${__INIT_ENV_LOADED:-}" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" + __INIT_ENV_LOADED=1 +fi + +# shellcheck disable=SC1090 +. "$INIT_ENV" +# shellcheck disable=SC1091 +. "$TOOLS/functestlib.sh" +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/lib_pkg_provider.sh" + +TESTNAME="RMTFS_Partition_Read_Validation" +RES_FILE="$SCRIPT_DIR/$TESTNAME.res" +PARTITIONS="${RMTFS_PARTITIONS:-modemst1 modemst2 fsc fsg}" + +usage() { + echo "Usage: $0 [--partitions 'modemst1 modemst2 ...']" +} + +while [ "$#" -gt 0 ]; do + case "$1" in + --partitions) + if [ "$#" -lt 2 ]; then + log_fail "--partitions requires a value" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + fi + PARTITIONS="$2" + shift 2 + ;; + --partitions=*) + PARTITIONS=${1#--partitions=} + shift + ;; + -h|--help) + usage + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 + ;; + *) + log_fail "Unknown argument: $1" + usage + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 + ;; + esac +done + +rm -f "$RES_FILE" + +# shellcheck disable=SC2317 +cleanup_on_exit() { + rmtfs_runtime_cleanup >/dev/null 2>&1 || true +} + +trap cleanup_on_exit 0 +trap 'exit 1' 1 2 15 + +RMTFS_RUNTIME_DIR="$SCRIPT_DIR/rmtfs_runtime" +rmtfs_runtime_prepare "$RMTFS_RUNTIME_DIR" +PREPARE_RC=$? + +if [ "$PREPARE_RC" -eq 2 ]; then + log_skip "$TESTNAME SKIP: RMTFS is not applicable on this platform" + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 +fi + +if [ "$PREPARE_RC" -ne 0 ]; then + log_fail "$TESTNAME FAIL: unable to prepare the RMTFS runtime snapshot" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 +fi + +find_partition() { + fp_label="$1" + for fp_dir in /dev/disk/by-partlabel /dev/disk/by-name /dev/block/by-name /dev/block/platform/*/by-name; do + [ -e "$fp_dir/$fp_label" ] || continue + fp_resolved=$(readlink -f "$fp_dir/$fp_label" 2>/dev/null || true) + if [ -z "$fp_resolved" ]; then + fp_resolved="$fp_dir/$fp_label" + fi + printf '%s\n' "$fp_resolved" + return 0 + done + return 1 +} + +OS_ID=$(pkg_detect_os_id 2>/dev/null || echo unknown) +ERROR_LOG="$SCRIPT_DIR/partition_read_errors.log" +rm -f "$ERROR_LOG" "$RES_FILE" +PASS_COUNT=0 +FAIL_COUNT=0 +SKIP_COUNT=0 +FOUND_COUNT=0 +log_info "Starting $TESTNAME on OS=$OS_ID" +log_info "Reads are limited to one 512-byte sector and partition contents are never logged" + +if [ "$RMTFS_MODULE_LOAD_ATTEMPTED" -eq 1 ] && \ + [ "$RMTFS_MODULE_LOAD_FAILED" -eq 1 ]; then + log_fail "$TESTNAME FAIL: the initially unloaded rmtfs_mem module could not be loaded" + FAIL_COUNT=$((FAIL_COUNT + 1)) +fi + +if [ "$RMTFS_MAINLINE_APPLICABLE" -eq 1 ]; then + rmtfs_start_service_if_available || true +fi +PARTITION_MODE=0 +PARTITION_MODE_KNOWN=0 +if rmtfs_process_running rmtfs; then + if rmtfs_daemon_cmdline_readable; then + PARTITION_MODE_KNOWN=1 + if rmtfs_daemon_uses_partitions; then + PARTITION_MODE=1 + log_info "The running rmtfs daemon uses raw partition mode" + else + log_info "The running rmtfs daemon uses directory-backed storage" + fi + else + log_warn "The running rmtfs daemon command line is not readable, storage mode could not be determined" + fi +fi + +for partition in $PARTITIONS; do + device=$(find_partition "$partition" 2>/dev/null || true) + if [ -z "$device" ]; then + log_skip "$partition: partition label not present" + SKIP_COUNT=$((SKIP_COUNT + 1)) + continue + fi + FOUND_COUNT=$((FOUND_COUNT + 1)) + if [ ! -b "$device" ]; then + log_fail "$partition: resolved path is not a block device: $device" + FAIL_COUNT=$((FAIL_COUNT + 1)) + continue + fi + if dd if="$device" of=/dev/null bs=512 count=1 2>> "$ERROR_LOG"; then + log_pass "$partition: one-sector read completed successfully from $device" + PASS_COUNT=$((PASS_COUNT + 1)) + else + log_fail "$partition: read failed for $device" + FAIL_COUNT=$((FAIL_COUNT + 1)) + fi +done + +if [ "$FOUND_COUNT" -eq 0 ]; then + if [ "$PARTITION_MODE" -eq 1 ]; then + log_fail "$TESTNAME FAIL: rmtfs uses partition mode but no requested partition labels were present" + FAIL_COUNT=$((FAIL_COUNT + 1)) + elif rmtfs_process_running rmtfs && \ + [ "$PARTITION_MODE_KNOWN" -eq 1 ]; then + log_skip "$TESTNAME SKIP: the running rmtfs daemon uses a directory-backed deployment" + SKIP_COUNT=$((SKIP_COUNT + 1)) + elif rmtfs_process_running rmtfs; then + log_skip "$TESTNAME SKIP: the running rmtfs daemon storage mode could not be determined" + SKIP_COUNT=$((SKIP_COUNT + 1)) + elif [ "$RMTFS_DT_PRESENT" -eq 1 ]; then + log_fail "$TESTNAME FAIL: RMTFS hardware is applicable but no daemon or partition storage became available" + FAIL_COUNT=$((FAIL_COUNT + 1)) + else + log_skip "$TESTNAME SKIP: no requested RMTFS partition labels were present" + SKIP_COUNT=$((SKIP_COUNT + 1)) + fi +fi + +if rmtfs_runtime_cleanup; then + log_pass "Initial RMTFS module and service state restored" +else + log_fail "Unable to restore the initial RMTFS module or service state" + FAIL_COUNT=$((FAIL_COUNT + 1)) +fi + +log_info "Summary: found=$FOUND_COUNT pass=$PASS_COUNT fail=$FAIL_COUNT skip=$SKIP_COUNT" +if [ "$FAIL_COUNT" -gt 0 ]; then + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 +fi +if [ "$PASS_COUNT" -gt 0 ]; then + echo "$TESTNAME PASS" > "$RES_FILE" + exit 0 +fi +echo "$TESTNAME SKIP" > "$RES_FILE" +exit 0 diff --git a/Runner/suites/Kernel/Baseport/Storage/RMTFS_QMI_Service_Validation/README.md b/Runner/suites/Kernel/Baseport/Storage/RMTFS_QMI_Service_Validation/README.md new file mode 100644 index 000000000..365c2ad73 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Storage/RMTFS_QMI_Service_Validation/README.md @@ -0,0 +1,25 @@ +Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +SPDX-License-Identifier: BSD-3-Clause + +# RMTFS QMI Service Validation + +Uses `qrtr-lookup` to validate the service published by public +`linux-msm/rmtfs`: + +- service: `14` +- version: `1` +- instance: `0` + +The values can be overridden with `RMTFS_QMI_SERVICE`, `RMTFS_QMI_VERSION`, and +`RMTFS_QMI_INSTANCE` for a documented downstream implementation. + +On an applicable mainline platform, the suite temporarily loads `rmtfs_mem` +and starts the image-provided service when necessary. Failure to start the +daemon is a test failure. It returns `SKIP` for non-RMTFS platforms, legacy +`rmt_storage` deployments, or images without the optional `qrtr-lookup` +diagnostic. Module and service state are restored after the lookup. + +```sh +cd Runner +./run-test.sh RMTFS_QMI_Service_Validation +``` diff --git a/Runner/suites/Kernel/Baseport/Storage/RMTFS_QMI_Service_Validation/RMTFS_QMI_Service_Validation.yaml b/Runner/suites/Kernel/Baseport/Storage/RMTFS_QMI_Service_Validation/RMTFS_QMI_Service_Validation.yaml new file mode 100644 index 000000000..5e79841cb --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Storage/RMTFS_QMI_Service_Validation/RMTFS_QMI_Service_Validation.yaml @@ -0,0 +1,20 @@ +metadata: + name: RMTFS_QMI_Service_Validation + format: "Lava-Test Test Definition 1.0" + description: "Validates reversible RMTFS startup and QRTR registration for QMI service 14, version 1, instance 0." + os: + - linux + scope: + - functional + +params: + SERVICE_ID: "14" + SERVICE_VERSION: "1" + SERVICE_INSTANCE: "0" + +run: + steps: + - REPO_PATH=$PWD + - cd Runner/suites/Kernel/Baseport/Storage/RMTFS_QMI_Service_Validation + - RMTFS_QMI_SERVICE="${SERVICE_ID}" RMTFS_QMI_VERSION="${SERVICE_VERSION}" RMTFS_QMI_INSTANCE="${SERVICE_INSTANCE}" ./run.sh || true + - $REPO_PATH/Runner/utils/send-to-lava.sh RMTFS_QMI_Service_Validation.res diff --git a/Runner/suites/Kernel/Baseport/Storage/RMTFS_QMI_Service_Validation/run.sh b/Runner/suites/Kernel/Baseport/Storage/RMTFS_QMI_Service_Validation/run.sh new file mode 100755 index 000000000..38d49af3c --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Storage/RMTFS_QMI_Service_Validation/run.sh @@ -0,0 +1,136 @@ +#!/bin/sh +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause + +# ---------- Repo env + helpers ---------- +SCRIPT_DIR="$( + cd "$(dirname "$0")" || exit 1 + 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 + +if [ -z "$INIT_ENV" ]; then + echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + exit 1 +fi + +# Only source once (idempotent) +# NOTE: We intentionally **do not export** any new vars. They stay local to this shell. +if [ -z "${__INIT_ENV_LOADED:-}" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" + __INIT_ENV_LOADED=1 +fi + +# shellcheck disable=SC1090 +. "$INIT_ENV" +# shellcheck disable=SC1091 +. "$TOOLS/functestlib.sh" +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/lib_pkg_provider.sh" + +TESTNAME="RMTFS_QMI_Service_Validation" +RES_FILE="$SCRIPT_DIR/$TESTNAME.res" +SERVICE_ID="${RMTFS_QMI_SERVICE:-14}" +SERVICE_VERSION="${RMTFS_QMI_VERSION:-1}" +SERVICE_INSTANCE="${RMTFS_QMI_INSTANCE:-0}" +rm -f "$RES_FILE" + +# shellcheck disable=SC2317 +cleanup_on_exit() { + rmtfs_runtime_cleanup >/dev/null 2>&1 || true +} + +finish_result() { + final_result="$1" + + if ! rmtfs_runtime_cleanup; then + log_fail "$TESTNAME FAIL: unable to restore the initial RMTFS module or service state" + final_result="FAIL" + fi + + echo "$TESTNAME $final_result" > "$RES_FILE" + + if [ "$final_result" = "FAIL" ]; then + exit 1 + fi + + exit 0 +} + +trap cleanup_on_exit 0 +trap 'exit 1' 1 2 15 + +RMTFS_RUNTIME_DIR="$SCRIPT_DIR/rmtfs_runtime" +rmtfs_runtime_prepare "$RMTFS_RUNTIME_DIR" +PREPARE_RC=$? + +if [ "$PREPARE_RC" -eq 2 ]; then + log_skip "$TESTNAME SKIP: RMTFS is not applicable on this platform" + finish_result SKIP +fi + +if [ "$PREPARE_RC" -ne 0 ]; then + log_fail "$TESTNAME FAIL: unable to prepare the RMTFS runtime snapshot" + finish_result FAIL +fi + +if [ "$RMTFS_MAINLINE_APPLICABLE" -eq 0 ] && \ + [ "$RMTFS_LEGACY_APPLICABLE" -eq 1 ]; then + log_skip "$TESTNAME SKIP: the legacy RMTFS stack does not publish the public QRTR service" + finish_result SKIP +fi + +if [ "$RMTFS_MODULE_LOAD_ATTEMPTED" -eq 1 ] && \ + [ "$RMTFS_MODULE_LOAD_FAILED" -eq 1 ]; then + log_fail "$TESTNAME FAIL: the initially unloaded rmtfs_mem module could not be loaded" + finish_result FAIL +fi + +if ! rmtfs_start_service_if_available || \ + ! rmtfs_process_running rmtfs; then + log_fail "$TESTNAME FAIL: applicable RMTFS hardware was found but the rmtfs daemon did not start" + finish_result FAIL +fi + +if ! command -v qrtr-lookup >/dev/null 2>&1; then + log_skip "$TESTNAME SKIP: qrtr-lookup is not installed" + finish_result SKIP +fi + +OS_ID=$(pkg_detect_os_id 2>/dev/null || echo unknown) +RUN_LOG="$SCRIPT_DIR/qrtr_lookup_rmtfs.log" +rm -f "$RUN_LOG" "$RES_FILE" +log_info "Starting $TESTNAME on OS=$OS_ID" +log_info "Expecting public linux-msm/rmtfs registration: service=$SERVICE_ID version=$SERVICE_VERSION instance=$SERVICE_INSTANCE" + +qrtr-lookup "$SERVICE_ID" > "$RUN_LOG" 2>&1 +LOOKUP_RC=$? +while IFS= read -r line; do + if [ -n "$line" ]; then + log_info "[qrtr-lookup] $line" + fi +done < "$RUN_LOG" + +if [ "$LOOKUP_RC" -ne 0 ]; then + log_fail "$TESTNAME FAIL: qrtr-lookup returned $LOOKUP_RC" + finish_result FAIL +fi + +if awk -v service="$SERVICE_ID" -v version="$SERVICE_VERSION" -v instance="$SERVICE_INSTANCE" \ + 'NR > 1 && $1 == service && $2 == version && $3 == instance { found=1 } END { exit !found }' "$RUN_LOG"; then + log_pass "$TESTNAME PASS: RMTFS QMI service registration found" + finish_result PASS +fi + +log_fail "$TESTNAME FAIL: service $SERVICE_ID version $SERVICE_VERSION instance $SERVICE_INSTANCE was not registered" +finish_result FAIL diff --git a/Runner/suites/Kernel/Baseport/Storage/RMTFS_Shared_Memory_Validation/README.md b/Runner/suites/Kernel/Baseport/Storage/RMTFS_Shared_Memory_Validation/README.md new file mode 100644 index 000000000..1fdf029fa --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Storage/RMTFS_Shared_Memory_Validation/README.md @@ -0,0 +1,33 @@ +Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +SPDX-License-Identifier: BSD-3-Clause + +# RMTFS Shared Memory Validation + +Validates the shared-memory path used by RMTFS without mixing it with daemon, +QMI-registration, or partition-content checks. + +Coverage includes: + +- `CONFIG_QCOM_RMTFS_MEM` +- `rmtfs_mem`/`qcom_rmtfs_mem` module or built-in driver +- `qcom,rmtfs-mem` device-tree compatibility +- `/dev/qcom_rmtfs_mem*` plus `phys_addr` and `size` sysfs attributes +- `/dev/qcom_rmtfs_uio*` and legacy UIO devices named `rmtfs` +- reserved-memory plus `/dev/mem` fallback used by public `linux-msm/rmtfs` + +If the runtime device tree is applicable and `rmtfs_mem` is not loaded, the +suite logs the module file, loads it temporarily, validates the resulting +device and sysfs attributes, then unloads it. A module that was already loaded +is left loaded. Services activated by the temporary module insertion are also +restored to their initial state. + +Kernel configuration or module-package presence alone does not make RMTFS +applicable, which avoids false failures on supported `meta-qcom` machines whose +device trees do not contain an RMTFS region. + +```sh +cd Runner +./run-test.sh RMTFS_Shared_Memory_Validation +``` + +The suite is distribution-neutral and does not install packages. diff --git a/Runner/suites/Kernel/Baseport/Storage/RMTFS_Shared_Memory_Validation/RMTFS_Shared_Memory_Validation.yaml b/Runner/suites/Kernel/Baseport/Storage/RMTFS_Shared_Memory_Validation/RMTFS_Shared_Memory_Validation.yaml new file mode 100644 index 000000000..16e7a0b21 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Storage/RMTFS_Shared_Memory_Validation/RMTFS_Shared_Memory_Validation.yaml @@ -0,0 +1,15 @@ +metadata: + name: RMTFS_Shared_Memory_Validation + format: "Lava-Test Test Definition 1.0" + description: "Validates reversible rmtfs_mem loading plus mainline, UIO, and reserved-memory shared-memory paths." + os: + - linux + scope: + - functional + +run: + steps: + - REPO_PATH=$PWD + - cd Runner/suites/Kernel/Baseport/Storage/RMTFS_Shared_Memory_Validation + - ./run.sh || true + - $REPO_PATH/Runner/utils/send-to-lava.sh RMTFS_Shared_Memory_Validation.res diff --git a/Runner/suites/Kernel/Baseport/Storage/RMTFS_Shared_Memory_Validation/run.sh b/Runner/suites/Kernel/Baseport/Storage/RMTFS_Shared_Memory_Validation/run.sh new file mode 100755 index 000000000..882c9e882 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Storage/RMTFS_Shared_Memory_Validation/run.sh @@ -0,0 +1,230 @@ +#!/bin/sh +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause + +# ---------- Repo env + helpers ---------- +SCRIPT_DIR="$( + cd "$(dirname "$0")" || exit 1 + 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 + +if [ -z "$INIT_ENV" ]; then + echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + exit 1 +fi + +# Only source once (idempotent) +# NOTE: We intentionally **do not export** any new vars. They stay local to this shell. +if [ -z "${__INIT_ENV_LOADED:-}" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" + __INIT_ENV_LOADED=1 +fi + +# shellcheck disable=SC1090 +. "$INIT_ENV" +# shellcheck disable=SC1091 +. "$TOOLS/functestlib.sh" +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/lib_pkg_provider.sh" + +TESTNAME="RMTFS_Shared_Memory_Validation" +RES_FILE="$SCRIPT_DIR/$TESTNAME.res" +rm -f "$RES_FILE" + +PASS_COUNT=0 +FAIL_COUNT=0 +SKIP_COUNT=0 + +pass_case() { + log_pass "$1" + PASS_COUNT=$((PASS_COUNT + 1)) +} + +fail_case() { + log_fail "$1" + FAIL_COUNT=$((FAIL_COUNT + 1)) +} + +skip_case() { + log_skip "$1" + SKIP_COUNT=$((SKIP_COUNT + 1)) +} + +# shellcheck disable=SC2317 +cleanup_on_exit() { + rmtfs_runtime_cleanup >/dev/null 2>&1 || true +} + +trap cleanup_on_exit 0 +trap 'exit 1' 1 2 15 + +RMTFS_RUNTIME_DIR="$SCRIPT_DIR/rmtfs_runtime" +rmtfs_runtime_prepare "$RMTFS_RUNTIME_DIR" +PREPARE_RC=$? + +if [ "$PREPARE_RC" -eq 2 ]; then + log_skip "$TESTNAME SKIP: no RMTFS shared-memory implementation detected" + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 +fi + +if [ "$PREPARE_RC" -ne 0 ]; then + log_fail "$TESTNAME FAIL: unable to prepare the RMTFS runtime snapshot" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 +fi + +OS_ID=$(pkg_detect_os_id 2>/dev/null || echo unknown) +log_info "Starting $TESTNAME on OS=$OS_ID" + +log_info "--- TC-01: kernel configuration/module ---" +case "$RMTFS_CONFIG_VALUE" in + y) + pass_case "TC-01: CONFIG_QCOM_RMTFS_MEM=y" + ;; + m) + pass_case "TC-01: CONFIG_QCOM_RMTFS_MEM=m" + if [ -n "$RMTFS_MODULE_PATH" ]; then + pass_case "TC-01: rmtfs_mem module file is present: $RMTFS_MODULE_PATH" + elif [ "$RMTFS_MODULE_WAS_LOADED" -eq 1 ]; then + pass_case "TC-01: rmtfs_mem was already loaded when the test started" + else + skip_case "TC-01: module file is not visible, fallback shared-memory paths will be checked" + fi + ;; + n) + if [ "$RMTFS_MAINLINE_APPLICABLE" -eq 1 ]; then + fail_case "TC-01: RMTFS hardware is present but CONFIG_QCOM_RMTFS_MEM is disabled" + else + skip_case "TC-01: CONFIG_QCOM_RMTFS_MEM is not required by the legacy stack" + fi + ;; + *) + if [ "$RMTFS_LEGACY_APPLICABLE" -eq 1 ]; then + skip_case "TC-01: CONFIG_QCOM_RMTFS_MEM is not required by the legacy stack" + else + skip_case "TC-01: running-kernel configuration is unavailable" + fi + ;; +esac + +if [ "$RMTFS_MODULE_WAS_LOADED" -eq 1 ]; then + pass_case "TC-01: initially loaded rmtfs_mem module will not be removed" +elif [ "$RMTFS_MODULE_LOADED_BY_TEST" -eq 1 ]; then + pass_case "TC-01: rmtfs_mem loaded temporarily for this validation" +elif [ "$RMTFS_MODULE_LOAD_ATTEMPTED" -eq 1 ] && \ + [ "$RMTFS_MODULE_LOAD_FAILED" -eq 1 ]; then + fail_case "TC-01: the initially unloaded rmtfs_mem module could not be loaded" +elif [ "$RMTFS_CONFIG_VALUE" = "y" ]; then + pass_case "TC-01: RMTFS memory driver is built into the kernel" +else + skip_case "TC-01: no active rmtfs_mem module, checking supported fallbacks" +fi + +log_info "--- TC-02: mainline qcom_rmtfs_mem device ---" +MAINLINE_DEVICE_FOUND=0 +for dev in /dev/qcom_rmtfs_mem*; do + if [ ! -e "$dev" ]; then + continue + fi + MAINLINE_DEVICE_FOUND=1 + if [ -c "$dev" ] && [ -r "$dev" ] && [ -w "$dev" ]; then + pass_case "TC-02: shared-memory character device is accessible: $dev" + else + fail_case "TC-02: shared-memory device is not an accessible character device: $dev" + fi + + dev_name=$(basename "$dev") + sys_path="/sys/class/rmtfs/$dev_name" + if [ ! -d "$sys_path" ] && command -v udevadm >/dev/null 2>&1; then + udev_path=$(udevadm info -q path -n "$dev" 2>/dev/null || true) + if [ -n "$udev_path" ]; then + sys_path="/sys$udev_path" + fi + fi + if [ -r "$sys_path/phys_addr" ] && [ -r "$sys_path/size" ]; then + pass_case "TC-02: phys_addr and size sysfs attributes are present for $dev_name" + else + fail_case "TC-02: phys_addr/size sysfs attributes are missing for $dev_name" + fi +done +if [ "$RMTFS_MAINLINE_APPLICABLE" -eq 1 ] && [ "$MAINLINE_DEVICE_FOUND" -eq 0 ]; then + skip_case "TC-02: no qcom_rmtfs_mem device, checking supported UIO or /dev/mem fallback" +fi + +log_info "--- TC-03: UIO shared-memory fallback ---" +UIO_FOUND=0 +for uio_name in /sys/class/uio/uio*/name; do + if [ ! -r "$uio_name" ]; then + continue + fi + if ! grep -qi '^rmtfs$' "$uio_name"; then + continue + fi + UIO_FOUND=1 + uio_dir=$(dirname "$uio_name") + uio_node="/dev/$(basename "$uio_dir")" + if [ -c "$uio_node" ] && [ -r "$uio_dir/maps/map0/addr" ] && [ -r "$uio_dir/maps/map0/size" ]; then + pass_case "TC-03: legacy RMTFS UIO device and map0 metadata are present: $uio_node" + else + fail_case "TC-03: RMTFS UIO mapping is incomplete: $uio_dir" + fi +done +for dev in /dev/qcom_rmtfs_uio*; do + if [ ! -e "$dev" ]; then + continue + fi + UIO_FOUND=1 + if [ -c "$dev" ] && [ -r "$dev" ] && [ -w "$dev" ]; then + pass_case "TC-03: qcom_rmtfs_uio compatibility device is accessible: $dev" + else + fail_case "TC-03: qcom_rmtfs_uio device is not accessible: $dev" + fi +done +if [ "$UIO_FOUND" -eq 0 ]; then + skip_case "TC-03: UIO fallback is not used on this platform" +fi + +log_info "--- TC-04: reserved-memory /dev/mem fallback ---" +if [ "$MAINLINE_DEVICE_FOUND" -eq 0 ] && [ "$UIO_FOUND" -eq 0 ]; then + if [ "$RMTFS_DT_PRESENT" -eq 1 ] && \ + [ -c /dev/mem ] && [ -r /dev/mem ] && [ -w /dev/mem ]; then + pass_case "TC-04: reserved-memory and /dev/mem fallback are available" + elif [ "$RMTFS_LEGACY_APPLICABLE" -eq 1 ]; then + skip_case "TC-04: legacy RMTFS does not expose a mainline shared-memory device" + else + fail_case "TC-04: no usable mainline, UIO, or /dev/mem RMTFS shared-memory path was found" + fi +else + skip_case "TC-04: /dev/mem fallback is not needed" +fi + +log_info "--- TC-05: state restoration ---" +if rmtfs_runtime_cleanup; then + log_pass "TC-05: initial RMTFS module and service state restored" +else + fail_case "TC-05: unable to restore the initial RMTFS module or service state" +fi + +log_info "Summary: pass=$PASS_COUNT fail=$FAIL_COUNT skip=$SKIP_COUNT" +if [ "$FAIL_COUNT" -gt 0 ]; then + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 +fi +if [ "$PASS_COUNT" -gt 0 ]; then + echo "$TESTNAME PASS" > "$RES_FILE" + exit 0 +fi +echo "$TESTNAME SKIP" > "$RES_FILE" +exit 0 diff --git a/Runner/suites/Kernel/Baseport/Storage/RMTFS_Validation/README.md b/Runner/suites/Kernel/Baseport/Storage/RMTFS_Validation/README.md new file mode 100644 index 000000000..11738d92e --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Storage/RMTFS_Validation/README.md @@ -0,0 +1,66 @@ +Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +SPDX-License-Identifier: BSD-3-Clause + +# RMTFS Validation + +Functional runtime health validation for the public +[linux-msm/rmtfs](https://github.com/linux-msm/rmtfs) daemon, with explicit +fallbacks for legacy `rmt_storage` deployments. + +Focused diagnostics are separate suites: + +- `RMTFS_QMI_Service_Validation` +- `RMTFS_Shared_Memory_Validation` +- `RMTFS_Partition_Read_Validation` + +## Applicability + +RMTFS is not present on every Qualcomm platform. Applicability is determined +from the runtime `qcom,rmtfs-mem` device tree, an existing shared-memory +device, or an active RMTFS implementation. `CONFIG_QCOM_RMTFS_MEM` and an +installed module file are logged but are not treated as hardware evidence, +because those build artifacts are shared by `meta-qcom` machines with and +without RMTFS hardware. + +The current public `meta-qcom` machine set was reviewed against the public +`qualcomm-linux/kernel` device trees. The test intentionally uses runtime DT +evidence rather than a machine-name allowlist so new machines and downstream +device-tree variants remain supported. + +## Module and service lifecycle + +When applicable hardware is present and `rmtfs_mem` is not loaded, the suite: + +1. Locates and logs the running-kernel module file. +2. Loads it using repository module helpers. +3. Waits for the matching `/dev/qcom_rmtfs_mem*` device and starts the image-provided service when needed. +4. Runs functional validation. +5. Stops only a service activated during the test and unloads only a module loaded by the test. + +An initially loaded module and initially active service are never removed. + +## Test cases + +| ID | Validation | +|---|---| +| TC-00 | Runtime DT, module presence/load, and usable shared-memory interface | +| TC-01 | Mainline `rmtfs` or legacy `rmt_storage` service/process health | +| TC-02 | Binary availability matching the detected implementation | +| TC-03 | QRTR or legacy IPC transport without assuming `/dev/qrtr` exists | +| TC-04 | Relevant kernel-log error scan with retained snapshot and filtered error logs | +| TC-05 | Restoration of the initial module and service state | + +## Distribution support + +- Yocto: validates the image-provided `rmtfs` recipe and service. +- Debian/Ubuntu: supports the public `rmtfs` package and systemd layout. +- CentOS/RHEL family: validates image-provided RPM content without assuming an unverified package name. + +No package manager or network operation is performed on the target. + +## Run + +```sh +cd Runner +./run-test.sh RMTFS_Validation +``` diff --git a/Runner/suites/Kernel/Baseport/Storage/RMTFS_Validation/RMTFS_Validation.yaml b/Runner/suites/Kernel/Baseport/Storage/RMTFS_Validation/RMTFS_Validation.yaml new file mode 100644 index 000000000..033c7c1bb --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Storage/RMTFS_Validation/RMTFS_Validation.yaml @@ -0,0 +1,15 @@ +metadata: + name: RMTFS_Validation + format: "Lava-Test Test Definition 1.0" + description: "Functional RMTFS module, shared-memory, daemon, transport, and kernel-log validation with state restoration." + os: + - linux + scope: + - functional + +run: + steps: + - REPO_PATH=$PWD + - cd Runner/suites/Kernel/Baseport/Storage/RMTFS_Validation + - ./run.sh || true + - $REPO_PATH/Runner/utils/send-to-lava.sh RMTFS_Validation.res diff --git a/Runner/suites/Kernel/Baseport/Storage/RMTFS_Validation/run.sh b/Runner/suites/Kernel/Baseport/Storage/RMTFS_Validation/run.sh new file mode 100755 index 000000000..dbe874ce8 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Storage/RMTFS_Validation/run.sh @@ -0,0 +1,275 @@ +#!/bin/sh +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause + +# ---------- Repo env + helpers ---------- +SCRIPT_DIR="$( + cd "$(dirname "$0")" || exit 1 + 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 + +if [ -z "$INIT_ENV" ]; then + echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + exit 1 +fi + +# Only source once (idempotent) +# NOTE: We intentionally **do not export** any new vars. They stay local to this shell. +if [ -z "${__INIT_ENV_LOADED:-}" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" + __INIT_ENV_LOADED=1 +fi + +# shellcheck disable=SC1090 +. "$INIT_ENV" +# shellcheck disable=SC1091 +. "$TOOLS/functestlib.sh" +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/lib_pkg_provider.sh" + +TESTNAME="RMTFS_Validation" +RES_FILE="$SCRIPT_DIR/$TESTNAME.res" + +test_path=$(find_test_case_by_name "$TESTNAME" 2>/dev/null || true) +if [ -n "$test_path" ]; then + cd "$test_path" || exit 1 +else + cd "$SCRIPT_DIR" || exit 1 +fi +rm -f "$RES_FILE" + +PASS_COUNT=0 +FAIL_COUNT=0 +SKIP_COUNT=0 + +pass_case() { + log_pass "$1" + PASS_COUNT=$((PASS_COUNT + 1)) +} + +fail_case() { + log_fail "$1" + FAIL_COUNT=$((FAIL_COUNT + 1)) +} + +skip_case() { + log_skip "$1" + SKIP_COUNT=$((SKIP_COUNT + 1)) +} + +# shellcheck disable=SC2317 +cleanup_on_exit() { + rmtfs_runtime_cleanup >/dev/null 2>&1 || true +} + +trap cleanup_on_exit 0 +trap 'exit 1' 1 2 15 + +RMTFS_RUNTIME_DIR="$SCRIPT_DIR/rmtfs_runtime" +rmtfs_runtime_prepare "$RMTFS_RUNTIME_DIR" +PREPARE_RC=$? + +if [ "$PREPARE_RC" -eq 2 ]; then + log_skip "$TESTNAME SKIP: RMTFS is not applicable on this platform" + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 +fi + +if [ "$PREPARE_RC" -ne 0 ]; then + log_fail "$TESTNAME FAIL: unable to prepare the RMTFS runtime snapshot" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 +fi + +OS_ID=$(pkg_detect_os_id 2>/dev/null || echo unknown) +detect_platform >/dev/null 2>&1 || true +log_info "Starting $TESTNAME on OS=$OS_ID platform=${PLATFORM_TARGET:-unknown}" +log_info "No package installation is attempted because all supported distributions use image-provided RMTFS components" + +log_info "--- TC-00: kernel module and shared-memory runtime ---" +if [ "$RMTFS_LEGACY_APPLICABLE" -eq 1 ] && \ + [ "$RMTFS_MAINLINE_APPLICABLE" -eq 0 ]; then + pass_case "TC-00: legacy RMTFS runtime evidence detected" +else + if [ "$RMTFS_DT_PRESENT" -eq 1 ]; then + pass_case "TC-00: qcom,rmtfs-mem is present in the runtime device tree" + fi + + if [ "$RMTFS_MODULE_WAS_LOADED" -eq 1 ]; then + pass_case "TC-00: rmtfs_mem was already loaded and will remain loaded" + elif [ "$RMTFS_MODULE_LOADED_BY_TEST" -eq 1 ]; then + pass_case "TC-00: rmtfs_mem was loaded temporarily for functional validation" + elif [ "$RMTFS_MODULE_LOAD_ATTEMPTED" -eq 1 ] && \ + [ "$RMTFS_MODULE_LOAD_FAILED" -eq 1 ]; then + fail_case "TC-00: the initially unloaded rmtfs_mem module could not be loaded" + elif [ "$RMTFS_CONFIG_VALUE" = "y" ]; then + pass_case "TC-00: CONFIG_QCOM_RMTFS_MEM=y, a separate module is not required" + fi + + if [ -n "$RMTFS_MAINLINE_DEVICE" ]; then + pass_case "TC-00: shared-memory character device found: $RMTFS_MAINLINE_DEVICE" + elif [ -n "$RMTFS_UIO_DEVICE" ]; then + pass_case "TC-00: RMTFS UIO shared-memory device found: $RMTFS_UIO_DEVICE" + elif [ "$RMTFS_DT_PRESENT" -eq 1 ] && \ + [ -c /dev/mem ] && [ -r /dev/mem ] && [ -w /dev/mem ]; then + pass_case "TC-00: reserved-memory /dev/mem fallback is available" + elif rmtfs_process_running rmtfs; then + pass_case "TC-00: the running rmtfs daemon has an initialized shared-memory backend" + else + fail_case "TC-00: no usable RMTFS shared-memory runtime appeared after module validation" + fi +fi + +if [ "$RMTFS_MAINLINE_APPLICABLE" -eq 1 ]; then + rmtfs_start_service_if_available || true +fi + +log_info "--- TC-01: daemon/service health ---" +DAEMON_RUNNING=0 +ACTIVE_UNIT="" +if [ "$RMTFS_MAINLINE_APPLICABLE" -eq 1 ]; then + ACTIVE_UNIT=$(rmtfs_get_active_service_unit 2>/dev/null || true) + if rmtfs_process_running rmtfs; then + DAEMON_RUNNING=1 + fi +fi +if [ "$RMTFS_LEGACY_APPLICABLE" -eq 1 ] && [ "$DAEMON_RUNNING" -eq 0 ]; then + for unit in rmt_storage qcom-rmt-storage; do + if systemd_service_exists "$unit" && systemd_service_is_active "$unit"; then + ACTIVE_UNIT="$unit" + DAEMON_RUNNING=1 + break + fi + done + if rmtfs_process_running rmt_storage; then + DAEMON_RUNNING=1 + fi +fi + +if [ "$DAEMON_RUNNING" -eq 1 ]; then + pass_case "TC-01: RMTFS daemon is running${ACTIVE_UNIT:+ via $ACTIVE_UNIT}" +else + SYSV_FOUND=0 + for init_script in /etc/init.d/rmtfs /etc/init.d/rmt_storage; do + if [ -x "$init_script" ]; then + SYSV_FOUND=1 + if "$init_script" status >/dev/null 2>&1; then + pass_case "TC-01: SysV RMTFS service is running: $init_script" + DAEMON_RUNNING=1 + else + fail_case "TC-01: SysV RMTFS service is not running: $init_script" + fi + break + fi + done + if [ "$SYSV_FOUND" -ne 1 ]; then + fail_case "TC-01: RMTFS hardware is present but no rmtfs/rmt_storage daemon is running" + fi +fi + +log_info "--- TC-02: daemon binary availability ---" +RMTFS_BIN=$(command -v rmtfs 2>/dev/null || true) +LEGACY_BIN=$(command -v rmt_storage 2>/dev/null || true) +if [ -z "$RMTFS_BIN" ]; then + for candidate in \ + /usr/bin/rmtfs \ + /usr/sbin/rmtfs \ + /usr/local/bin/rmtfs \ + /sbin/rmtfs + do + if [ -x "$candidate" ]; then + RMTFS_BIN="$candidate" + break + fi + done +fi +if [ -z "$LEGACY_BIN" ]; then + for candidate in \ + /usr/bin/rmt_storage \ + /usr/sbin/rmt_storage \ + /sbin/rmt_storage + do + if [ -x "$candidate" ]; then + LEGACY_BIN="$candidate" + break + fi + done +fi +if [ "$RMTFS_MAINLINE_APPLICABLE" -eq 1 ] && [ -n "$RMTFS_BIN" ]; then + pass_case "TC-02: mainline rmtfs binary found: $RMTFS_BIN" +elif [ "$RMTFS_LEGACY_APPLICABLE" -eq 1 ] && [ -n "$LEGACY_BIN" ]; then + pass_case "TC-02: legacy rmt_storage binary found: $LEGACY_BIN" +else + fail_case "TC-02: no binary matching the detected RMTFS implementation was found" +fi + +log_info "--- TC-03: QMI transport availability ---" +if [ "$RMTFS_MAINLINE_APPLICABLE" -eq 1 ]; then + if [ -e /sys/bus/qrtr ] || [ -e /proc/net/qrtr ] || [ -e /sys/module/qrtr ] || \ + is_module_loaded qrtr || rmtfs_process_running rmtfs; then + pass_case "TC-03: QRTR transport is available (QRTR does not require /dev/qrtr)" + else + fail_case "TC-03: mainline RMTFS detected but no QRTR transport evidence was found" + fi +elif [ -e /dev/msm_ipc ]; then + pass_case "TC-03: legacy IPC Router device found: /dev/msm_ipc" +else + skip_case "TC-03: legacy transport interface could not be identified" +fi + +log_info "--- TC-04: kernel log health ---" +RMTFS_DMESG_DIR="$SCRIPT_DIR/rmtfs_dmesg" +RMTFS_DMESG_MODULES='rmtfs|rmt_storage|qcom_rmtfs_mem|qcom-rmtfs-mem|rmtfs_mem|msm_sharedmem' +RMTFS_DMESG_EXCLUDE='optional|not supported|deferred probe' +RMTFS_DMESG_ERRORS_FOUND=0 +RMTFS_DMESG_READABLE=0 + +if scan_dmesg_errors \ + "$RMTFS_DMESG_DIR" \ + "$RMTFS_DMESG_MODULES" \ + "$RMTFS_DMESG_EXCLUDE"; then + RMTFS_DMESG_ERRORS_FOUND=1 +fi +if [ -s "$RMTFS_DMESG_DIR/dmesg_snapshot.log" ]; then + RMTFS_DMESG_READABLE=1 +fi + +if [ "$RMTFS_DMESG_READABLE" -eq 1 ]; then + if [ "$RMTFS_DMESG_ERRORS_FOUND" -eq 1 ]; then + fail_case "TC-04: RMTFS errors found in dmesg, see rmtfs_dmesg/dmesg_errors.log" + else + pass_case "TC-04: no relevant RMTFS errors found in dmesg" + fi +else + skip_case "TC-04: dmesg is not readable by the current test user" +fi + +log_info "--- TC-05: state restoration ---" +if rmtfs_runtime_cleanup; then + log_pass "TC-05: initial RMTFS module and service state restored" +else + fail_case "TC-05: unable to restore the initial RMTFS module or service state" +fi + +log_info "Summary: pass=$PASS_COUNT fail=$FAIL_COUNT skip=$SKIP_COUNT" +if [ "$FAIL_COUNT" -gt 0 ]; then + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 +fi +if [ "$PASS_COUNT" -gt 0 ]; then + echo "$TESTNAME PASS" > "$RES_FILE" + exit 0 +fi +echo "$TESTNAME SKIP" > "$RES_FILE" +exit 0 diff --git a/Runner/utils/functestlib.sh b/Runner/utils/functestlib.sh index d113169b2..f01d7690e 100755 --- a/Runner/utils/functestlib.sh +++ b/Runner/utils/functestlib.sh @@ -28,35 +28,83 @@ get_kernel_log() { fi } -# Locate a kernel module (.ko) file by name -# Tries to find it under current kernel version first, then all module trees +# Locate a kernel module file by name. +# Supports uncompressed and commonly compressed module formats. +# Tries the running kernel first, then all module trees. find_kernel_module() { module_name="$1" kver=$(uname -r) - # Attempt to find module under the currently running kernel - module_path=$(find "/lib/modules/$kver" -name "${module_name}.ko" 2>/dev/null | head -n 1) + module_path="" + + if [ -d "/lib/modules/$kver" ]; then + module_path=$( + find "/lib/modules/$kver" -type f \ + \( -name "${module_name}.ko" \ + -o -name "${module_name}.ko.xz" \ + -o -name "${module_name}.ko.gz" \ + -o -name "${module_name}.ko.zst" \) \ + 2>/dev/null | + head -n 1 + ) + fi - # If not found, search all available module directories - if [ -z "$module_path" ]; then - log_warn "Module not found under /lib/modules/$kver, falling back to full search in /lib/modules/" - module_path=$(find /lib/modules/ -name "${module_name}.ko" 2>/dev/null | head -n 1) + if [ -z "$module_path" ] && [ -d /lib/modules ]; then + log_warn "Module $module_name not found under /lib/modules/$kver, searching all module trees" + module_path=$( + find /lib/modules -type f \ + \( -name "${module_name}.ko" \ + -o -name "${module_name}.ko.xz" \ + -o -name "${module_name}.ko.gz" \ + -o -name "${module_name}.ko.zst" \) \ + 2>/dev/null | + head -n 1 + ) - # Warn if found outside current kernel version if [ -n "$module_path" ]; then - found_version=$(echo "$module_path" | cut -d'/' -f4) + found_version=${module_path#/lib/modules/} + found_version=${found_version%%/*} if [ "$found_version" != "$kver" ]; then - log_warn "Found ${module_name}.ko under $found_version, not under current kernel ($kver)" + log_warn "Found $module_name under kernel $found_version, running kernel is $kver" fi fi fi - echo "$module_path" + + printf '%s\n' "$module_path" } # Check if a kernel module is currently loaded is_module_loaded() { module_name="$1" - /sbin/lsmod | awk '{print $1}' | grep -q "^${module_name}$" + + if [ -r /proc/modules ]; then + awk -v target="$module_name" ' + $1 == target { + found = 1 + exit + } + END { + exit !found + } + ' /proc/modules + return $? + fi + + if command -v lsmod >/dev/null 2>&1; then + lsmod 2>/dev/null | + awk -v target="$module_name" ' + $1 == target { + found = 1 + exit + } + END { + exit !found + } + ' + return $? + fi + + return 1 } # load_kernel_module [params...] @@ -64,43 +112,53 @@ is_module_loaded() { # 2) Try insmod [params] # 3) If that fails, try modprobe [params] load_kernel_module() { - module_path="$1"; shift + module_path="$1" + shift params="$*" - module_name=$(basename "$module_path" .ko) + module_file=$(basename "$module_path") + module_name=${module_file%%.ko*} + insmod_bin=$(command -v insmod 2>/dev/null || true) + modprobe_bin=$(command -v modprobe 2>/dev/null || true) if is_module_loaded "$module_name"; then log_info "Module $module_name is already loaded" return 0 fi - if [ ! -f "$module_path" ]; then + if [ -z "$module_path" ] || [ ! -f "$module_path" ]; then log_error "Module file not found: $module_path" - # still try modprobe if it exists in modules directory - else - log_info "Loading module via insmod: $module_path $params" - if /sbin/insmod "$module_path" "$params" 2>insmod_err.log; then + elif [ -n "$insmod_bin" ]; then + log_info "Loading module via insmod: $module_path${params:+ $params}" + if "$insmod_bin" "$module_path" "$@" 2>insmod_err.log; then log_info "Module $module_name loaded successfully via insmod" return 0 - else - log_warn "insmod failed: $(cat insmod_err.log)" fi + log_warn "insmod failed: $(cat insmod_err.log)" + else + log_warn "insmod is not available, trying modprobe" fi - # fallback to modprobe - log_info "Falling back to modprobe $module_name $params" - if /sbin/modprobe "$module_name" "$params" 2>modprobe_err.log; then + if [ -z "$modprobe_bin" ]; then + log_error "modprobe is not available for module $module_name" + return 1 + fi + + log_info "Loading module via modprobe: $module_name${params:+ $params}" + if "$modprobe_bin" "$module_name" "$@" 2>modprobe_err.log; then log_info "Module $module_name loaded successfully via modprobe" return 0 - else - log_error "modprobe failed: $(cat modprobe_err.log)" - return 1 fi + + log_error "modprobe failed: $(cat modprobe_err.log)" + return 1 } # Remove a kernel module by name with optional forced removal unload_kernel_module() { module_name="$1" force="$2" + rmmod_bin=$(command -v rmmod 2>/dev/null || true) + modprobe_bin=$(command -v modprobe 2>/dev/null || true) if ! is_module_loaded "$module_name"; then log_info "Module $module_name is not loaded, skipping unload" @@ -108,29 +166,35 @@ unload_kernel_module() { fi log_info "Attempting to remove module: $module_name" - if /sbin/rmmod "$module_name" 2>rmmod_err.log; then - log_info "Module $module_name removed via rmmod" - return 0 + if [ -n "$rmmod_bin" ]; then + if "$rmmod_bin" "$module_name" 2>rmmod_err.log; then + log_info "Module $module_name removed via rmmod" + return 0 + fi + log_warn "rmmod failed: $(cat rmmod_err.log)" + else + log_warn "rmmod is not available, trying modprobe -r" fi - log_warn "rmmod failed: $(cat rmmod_err.log)" - log_info "Trying modprobe -r as fallback" - if /sbin/modprobe -r "$module_name" 2>modprobe_err.log; then - log_info "Module $module_name removed via modprobe" - return 0 + if [ -n "$modprobe_bin" ]; then + log_info "Trying modprobe -r for module $module_name" + if "$modprobe_bin" -r "$module_name" 2>modprobe_err.log; then + log_info "Module $module_name removed via modprobe" + return 0 + fi + log_warn "modprobe -r failed: $(cat modprobe_err.log)" + else + log_warn "modprobe is not available for module removal" fi - log_warn "modprobe -r failed: $(cat modprobe_err.log)" - - if [ "$force" = "true" ]; then + if [ "$force" = "true" ] && [ -n "$rmmod_bin" ]; then log_warn "Trying forced rmmod: $module_name" - if /sbin/rmmod -f "$module_name" 2>>rmmod_err.log; then + if "$rmmod_bin" -f "$module_name" 2>>rmmod_err.log; then log_info "Module $module_name force removed" return 0 - else - log_error "Forced rmmod failed: $(cat rmmod_err.log)" - return 1 fi + log_error "Forced rmmod failed: $(cat rmmod_err.log)" + return 1 fi log_error "Unable to unload module: $module_name" @@ -4098,7 +4162,7 @@ dt_confirm_node_or_compatible_all() { case "$pattern" in *,*) # label as chip (the part after the last comma) to avoid "swapped" look chip_label="${pattern##*,}" - comp_file="$(grep -r -s -i -F -l -m1 -- "$pattern" "$DT_ROOT" 2>/dev/null | grep '/compatible$' -m1 || true)" + comp_file="$(grep -r -s -i -a -F -l -m1 -- "$pattern" "$DT_ROOT" 2>/dev/null | grep '/compatible$' -m1 || true)" if [ -n "$comp_file" ]; then comp_print="$(tr '\0' ' ' <"$comp_file" 2>/dev/null)" comp_csv="$(tr '\0' ',' <"$comp_file" 2>/dev/null)"; comp_csv="${comp_csv%,}" @@ -4111,7 +4175,7 @@ dt_confirm_node_or_compatible_all() { fi ;; *) - cand_list="$(grep -r -s -i -F -l -- "$pattern" "$DT_ROOT" 2>/dev/null | grep '/compatible$' | head -n 64 || true)" + cand_list="$(grep -r -s -i -a -F -l -- "$pattern" "$DT_ROOT" 2>/dev/null | grep '/compatible$' | head -n 64 || true)" if [ -n "$cand_list" ]; then for comp_file in $cand_list; do hit=0 @@ -4148,6 +4212,692 @@ dt_confirm_node_or_compatible() { dt_confirm_node_or_compatible_all "$@" } +############################################################################### +# Qualcomm MinkIPC package preparation +############################################################################### + +# Ensure the MinkIPC runtime and public test clients on Debian and Ubuntu. +# Yocto, CentOS, and other images continue using image-provided components. +# +# qli-staging currently publishes the MinkIPC packages in its trixie suite. +# The package-provider library performs all source setup, network handling, +# package installation, and post-install verification. +minkipc_prepare_test_packages() { + mptp_os_id="unknown" + + if command -v pkg_detect_os_id >/dev/null 2>&1; then + mptp_os_id=$(pkg_detect_os_id 2>/dev/null || printf '%s\n' unknown) + elif [ -r /etc/os-release ]; then + mptp_os_id=$( + sed -n 's/^ID=//p' /etc/os-release 2>/dev/null | + sed -n '1p' | + sed 's/^"//; s/"$//' | + tr '[:upper:]' '[:lower:]' + ) + fi + + [ -n "$mptp_os_id" ] || mptp_os_id="unknown" + + case "$mptp_os_id" in + debian|ubuntu) + ;; + qcom-distro|poky|openembedded|oe) + log_info "Native image detected, MinkIPC package preparation is not required" + return 0 + ;; + centos|rhel|fedora) + log_info "RPM image detected, using image-provided MinkIPC components" + return 0 + ;; + *) + log_info "MinkIPC package preparation is not enabled for os=$mptp_os_id" + return 0 + ;; + esac + + for mptp_helper in \ + pkg_provider_init \ + pkg_active_provider \ + pkg_ensure_required_package_set_present; do + if ! command -v "$mptp_helper" >/dev/null 2>&1; then + log_fail "Required MinkIPC package helper is unavailable: $mptp_helper" + return 1 + fi + done + + pkg_provider_init || return 1 + + mptp_provider=$(pkg_active_provider 2>/dev/null || printf '%s\n' check) + if [ "$mptp_provider" != "apt" ]; then + log_fail "MinkIPC package preparation requires apt on os=$mptp_os_id, provider=$mptp_provider" + return 1 + fi + + mptp_old_source="${PKG_APT_DEBUSINE_SOURCE-__unset__}" + mptp_old_suite="${PKG_APT_DEBUSINE_SUITE-__unset__}" + mptp_old_upgrade="${PKG_PACKAGE_SET_UPGRADE-__unset__}" + + PKG_APT_DEBUSINE_SOURCE="qli-staging" + PKG_APT_DEBUSINE_SUITE="trixie" + PKG_PACKAGE_SET_UPGRADE=0 + + if [ "$mptp_old_source" != "$PKG_APT_DEBUSINE_SOURCE" ] || + [ "$mptp_old_suite" != "$PKG_APT_DEBUSINE_SUITE" ]; then + rm -f "${PKG_APT_UPDATED_MARK:-/tmp/qcom_testkit_apt_updated}" \ + 2>/dev/null || true + fi + + log_info "Preparing MinkIPC packages, os=$mptp_os_id source=$PKG_APT_DEBUSINE_SOURCE suite=$PKG_APT_DEBUSINE_SUITE" + + pkg_ensure_required_package_set_present minkipc + mptp_rc=$? + + case "$mptp_old_source" in + __unset__) + unset PKG_APT_DEBUSINE_SOURCE + ;; + *) + PKG_APT_DEBUSINE_SOURCE="$mptp_old_source" + ;; + esac + + case "$mptp_old_suite" in + __unset__) + unset PKG_APT_DEBUSINE_SUITE + ;; + *) + PKG_APT_DEBUSINE_SUITE="$mptp_old_suite" + ;; + esac + + case "$mptp_old_upgrade" in + __unset__) + unset PKG_PACKAGE_SET_UPGRADE + ;; + *) + PKG_PACKAGE_SET_UPGRADE="$mptp_old_upgrade" + ;; + esac + + if [ "$mptp_rc" -ne 0 ]; then + log_fail "Failed to prepare the MinkIPC package set on os=$mptp_os_id" + return 1 + fi + + log_pass "MinkIPC package set is ready on os=$mptp_os_id" + return 0 +} + +############################################################################### +# Qualcomm RMTFS runtime helpers +############################################################################### + +# Read CONFIG_QCOM_RMTFS_MEM from the running-kernel configuration. +# Prints y, m, n, or unknown. +rmtfs_kernel_config_value() { + rmtfs_config_line="" + + if [ -r /proc/config.gz ]; then + if command -v zgrep >/dev/null 2>&1; then + rmtfs_config_line=$( + zgrep -E '^CONFIG_QCOM_RMTFS_MEM=(y|m)$|^# CONFIG_QCOM_RMTFS_MEM is not set$' /proc/config.gz \ + 2>/dev/null | + head -n 1 + ) + elif command -v gzip >/dev/null 2>&1; then + rmtfs_config_line=$( + gzip -dc /proc/config.gz 2>/dev/null | + grep -E '^CONFIG_QCOM_RMTFS_MEM=(y|m)$|^# CONFIG_QCOM_RMTFS_MEM is not set$' | + head -n 1 + ) + fi + elif [ -r "/boot/config-$(uname -r)" ]; then + rmtfs_config_line=$( + grep -E '^CONFIG_QCOM_RMTFS_MEM=(y|m)$|^# CONFIG_QCOM_RMTFS_MEM is not set$' \ + "/boot/config-$(uname -r)" 2>/dev/null | + head -n 1 + ) + fi + + case "$rmtfs_config_line" in + *=y) + printf '%s\n' y + ;; + *=m) + printf '%s\n' m + ;; + '# CONFIG_QCOM_RMTFS_MEM is not set') + printf '%s\n' n + ;; + *) + printf '%s\n' unknown + ;; + esac +} + +# Find an exact NUL-separated compatible token without relying on recursive +# grep binary-file behavior, which varies between GNU and BusyBox grep. +rmtfs_find_dt_compatible_file() { + rmtfs_search_root="$1" + rmtfs_compatible_token="$2" + + find "$rmtfs_search_root" -type f -name compatible 2>/dev/null | + while IFS= read -r rmtfs_compatible_file; do + if tr '\000' '\n' < "$rmtfs_compatible_file" 2>/dev/null | + grep -F -x -q "$rmtfs_compatible_token"; then + printf '%s\n' "$rmtfs_compatible_file" + break + fi + done +} + +# Check both Linux runtime device-tree locations for the RMTFS compatible. +rmtfs_dt_present() { + rmtfs_dt_root_seen=0 + + for rmtfs_dt_root in \ + /proc/device-tree \ + /sys/firmware/devicetree/base + do + if [ ! -d "$rmtfs_dt_root" ]; then + continue + fi + + rmtfs_dt_root_seen=1 + rmtfs_compatible_file=$( + rmtfs_find_dt_compatible_file \ + "$rmtfs_dt_root" \ + "qcom,rmtfs-mem" + ) + if [ -n "$rmtfs_compatible_file" ]; then + rmtfs_compatible_node=$(dirname "$rmtfs_compatible_file") + log_info "RMTFS device-tree compatible found: $rmtfs_compatible_node" + return 0 + fi + done + + if [ "$rmtfs_dt_root_seen" -eq 1 ]; then + log_info "RMTFS compatible qcom,rmtfs-mem was not found in the runtime device tree" + else + log_info "Runtime device-tree paths are not available" + fi + + return 1 +} + +rmtfs_find_mainline_device() { + for rmtfs_device in /dev/qcom_rmtfs_mem*; do + if [ -e "$rmtfs_device" ]; then + printf '%s\n' "$rmtfs_device" + return 0 + fi + done + + return 1 +} + +rmtfs_find_uio_device() { + for rmtfs_device in /dev/qcom_rmtfs_uio*; do + if [ -e "$rmtfs_device" ]; then + printf '%s\n' "$rmtfs_device" + return 0 + fi + done + + for rmtfs_uio_name in /sys/class/uio/uio*/name; do + if [ ! -r "$rmtfs_uio_name" ]; then + continue + fi + + if grep -qi '^rmtfs$' "$rmtfs_uio_name"; then + rmtfs_uio_dir=$(dirname "$rmtfs_uio_name") + printf '/dev/%s\n' "$(basename "$rmtfs_uio_dir")" + return 0 + fi + done + + return 1 +} + +rmtfs_process_running() { + get_pid "$1" >/dev/null 2>&1 +} + +rmtfs_wait_for_process() { + rmtfs_wait_name="$1" + rmtfs_wait_timeout="${2:-5}" + rmtfs_wait_count=0 + + while [ "$rmtfs_wait_count" -lt "$rmtfs_wait_timeout" ]; do + if rmtfs_process_running "$rmtfs_wait_name"; then + return 0 + fi + + sleep 1 + rmtfs_wait_count=$((rmtfs_wait_count + 1)) + done + + return 1 +} + +rmtfs_wait_for_process_exit() { + rmtfs_wait_name="$1" + rmtfs_wait_timeout="${2:-5}" + rmtfs_wait_count=0 + + while [ "$rmtfs_wait_count" -lt "$rmtfs_wait_timeout" ]; do + if ! rmtfs_process_running "$rmtfs_wait_name"; then + return 0 + fi + + sleep 1 + rmtfs_wait_count=$((rmtfs_wait_count + 1)) + done + + return 1 +} + +rmtfs_wait_for_mainline_device() { + rmtfs_wait_timeout="${1:-5}" + rmtfs_wait_count=0 + + while [ "$rmtfs_wait_count" -lt "$rmtfs_wait_timeout" ]; do + if rmtfs_find_mainline_device >/dev/null 2>&1; then + return 0 + fi + + sleep 1 + rmtfs_wait_count=$((rmtfs_wait_count + 1)) + done + + return 1 +} + +rmtfs_service_was_active() { + rmtfs_service_query="$1" + + case " $RMTFS_INITIAL_ACTIVE_UNITS " in + *" $rmtfs_service_query "*) + return 0 + ;; + *) + return 1 + ;; + esac +} + +rmtfs_get_active_service_unit() { + for rmtfs_service_unit in rmtfs rmtfs-dir; do + if systemd_service_exists "$rmtfs_service_unit" && \ + systemd_service_is_active "$rmtfs_service_unit"; then + printf '%s\n' "$rmtfs_service_unit" + return 0 + fi + done + + return 1 +} + +# Record a daemon or service that appeared after the initial snapshot. +rmtfs_record_runtime_activation() { + if [ "${RMTFS_RUNTIME_ACTIVATION_EXPECTED:-0}" -ne 1 ] || \ + [ "${RMTFS_DAEMON_WAS_RUNNING:-0}" -ne 0 ] || \ + ! rmtfs_process_running rmtfs; then + return 0 + fi + + if [ "${RMTFS_DAEMON_STARTED_BY_TEST:-0}" -eq 0 ]; then + RMTFS_DAEMON_STARTED_BY_TEST=1 + log_info "RMTFS runtime activation detected after the initial snapshot" + fi + + rmtfs_started_unit=$(rmtfs_get_active_service_unit 2>/dev/null || true) + if [ -n "$rmtfs_started_unit" ] && \ + ! rmtfs_service_was_active "$rmtfs_started_unit" && \ + [ "${RMTFS_SYSTEMD_STARTED_BY_TEST:-}" != "$rmtfs_started_unit" ]; then + RMTFS_SYSTEMD_STARTED_BY_TEST="$rmtfs_started_unit" + log_info "RMTFS service activation recorded for cleanup: $rmtfs_started_unit" + fi +} + +rmtfs_runtime_reset_state() { + RMTFS_MODULE_NAME="rmtfs_mem" + RMTFS_MODULE_PATH="" + RMTFS_MODULE_WAS_LOADED=0 + RMTFS_MODULE_LOADED_BY_TEST=0 + RMTFS_MODULE_LOAD_ATTEMPTED=0 + RMTFS_MODULE_LOAD_FAILED=0 + RMTFS_CONFIG_VALUE="unknown" + RMTFS_DT_PRESENT=0 + RMTFS_MAINLINE_DEVICE="" + RMTFS_UIO_DEVICE="" + RMTFS_MAINLINE_APPLICABLE=0 + RMTFS_LEGACY_APPLICABLE=0 + RMTFS_DAEMON_WAS_RUNNING=0 + RMTFS_DAEMON_STARTED_BY_TEST=0 + RMTFS_RUNTIME_ACTIVATION_EXPECTED=0 + RMTFS_INITIAL_ACTIVE_UNITS="" + RMTFS_SYSTEMD_STARTED_BY_TEST="" + RMTFS_SYSV_STARTED_BY_TEST="" + RMTFS_RUNTIME_PREPARED=0 +} + +# Snapshot RMTFS state and load rmtfs_mem only on applicable DT platforms. +# Return values: +# 0 - RMTFS is applicable and the runtime snapshot was prepared +# 1 - preparation failed +# 2 - RMTFS is not applicable on this runtime device tree +# State variables assigned here are consumed by the sourced RMTFS suites. +# shellcheck disable=SC2034 +rmtfs_runtime_prepare() { + rmtfs_output_dir="${1:-.}" + rmtfs_runtime_reset_state + + mkdir -p "$rmtfs_output_dir" 2>/dev/null || return 1 + + RMTFS_CONFIG_VALUE=$(rmtfs_kernel_config_value) + if [ "$RMTFS_CONFIG_VALUE" = "unknown" ]; then + log_info "CONFIG_QCOM_RMTFS_MEM value is unavailable" + else + log_info "Kernel configuration reports CONFIG_QCOM_RMTFS_MEM=$RMTFS_CONFIG_VALUE" + fi + + rmtfs_module_lookup=$( + find_kernel_module "$RMTFS_MODULE_NAME" 2>&1 + ) + printf '%s\n' "$rmtfs_module_lookup" > \ + "$rmtfs_output_dir/rmtfs_module_lookup.log" + RMTFS_MODULE_PATH=$( + printf '%s\n' "$rmtfs_module_lookup" | + awk '/^\// { print; exit }' + ) + + if [ -n "$RMTFS_MODULE_PATH" ]; then + case "$RMTFS_MODULE_PATH" in + "/lib/modules/$(uname -r)/"*) + log_info "RMTFS module file found: $RMTFS_MODULE_PATH" + ;; + *) + log_warn "Ignoring RMTFS module from a different kernel tree: $RMTFS_MODULE_PATH" + RMTFS_MODULE_PATH="" + ;; + esac + else + log_info "RMTFS module file was not found for kernel $(uname -r)" + fi + + if is_module_loaded "$RMTFS_MODULE_NAME"; then + RMTFS_MODULE_WAS_LOADED=1 + log_info "RMTFS module snapshot: $RMTFS_MODULE_NAME was already loaded" + else + log_info "RMTFS module snapshot: $RMTFS_MODULE_NAME was not loaded" + fi + + if rmtfs_process_running rmtfs; then + RMTFS_DAEMON_WAS_RUNNING=1 + RMTFS_MAINLINE_APPLICABLE=1 + log_info "RMTFS daemon snapshot: rmtfs was already running" + fi + + for rmtfs_service_unit in rmtfs rmtfs-dir; do + if systemd_service_exists "$rmtfs_service_unit" && \ + systemd_service_is_active "$rmtfs_service_unit"; then + RMTFS_INITIAL_ACTIVE_UNITS="${RMTFS_INITIAL_ACTIVE_UNITS} ${rmtfs_service_unit}" + RMTFS_DAEMON_WAS_RUNNING=1 + RMTFS_MAINLINE_APPLICABLE=1 + log_info "RMTFS service snapshot: $rmtfs_service_unit was already active" + fi + done + + if rmtfs_dt_present; then + RMTFS_DT_PRESENT=1 + RMTFS_MAINLINE_APPLICABLE=1 + fi + + RMTFS_MAINLINE_DEVICE=$(rmtfs_find_mainline_device 2>/dev/null || true) + if [ -n "$RMTFS_MAINLINE_DEVICE" ]; then + RMTFS_MAINLINE_APPLICABLE=1 + log_info "RMTFS shared-memory device found: $RMTFS_MAINLINE_DEVICE" + fi + + RMTFS_UIO_DEVICE=$(rmtfs_find_uio_device 2>/dev/null || true) + if [ -n "$RMTFS_UIO_DEVICE" ]; then + RMTFS_MAINLINE_APPLICABLE=1 + log_info "RMTFS UIO shared-memory device found: $RMTFS_UIO_DEVICE" + fi + + if is_module_loaded "$RMTFS_MODULE_NAME"; then + RMTFS_MAINLINE_APPLICABLE=1 + fi + + if is_module_loaded msm_sharedmem || rmtfs_process_running rmt_storage; then + RMTFS_LEGACY_APPLICABLE=1 + fi + + if [ "$RMTFS_MAINLINE_APPLICABLE" -eq 0 ] && \ + [ "$RMTFS_LEGACY_APPLICABLE" -eq 0 ]; then + log_info "RMTFS kernel support alone is not an applicability signal without runtime DT or device evidence" + RMTFS_RUNTIME_PREPARED=0 + return 2 + fi + + if [ "$RMTFS_DT_PRESENT" -eq 1 ] && \ + [ "$RMTFS_MODULE_WAS_LOADED" -eq 0 ] && \ + [ "$RMTFS_CONFIG_VALUE" != "y" ] && \ + [ "$RMTFS_CONFIG_VALUE" != "n" ]; then + if [ -n "$RMTFS_MODULE_PATH" ]; then + RMTFS_MODULE_LOAD_ATTEMPTED=1 + log_info "Loading $RMTFS_MODULE_NAME for applicable RMTFS device-tree hardware" + + if is_module_loaded "$RMTFS_MODULE_NAME"; then + RMTFS_MODULE_WAS_LOADED=1 + log_info "RMTFS module became loaded after the initial snapshot and will be preserved" + elif load_kernel_module "$RMTFS_MODULE_PATH"; then + if is_module_loaded "$RMTFS_MODULE_NAME"; then + RMTFS_MODULE_LOADED_BY_TEST=1 + RMTFS_RUNTIME_ACTIVATION_EXPECTED=1 + log_pass "RMTFS module loaded by the test: $RMTFS_MODULE_NAME" + else + RMTFS_MODULE_LOAD_FAILED=1 + log_fail "RMTFS module load returned success but $RMTFS_MODULE_NAME is not listed as loaded" + fi + else + RMTFS_MODULE_LOAD_FAILED=1 + log_fail "Unable to load RMTFS module: $RMTFS_MODULE_PATH" + fi + elif [ "$RMTFS_CONFIG_VALUE" = "m" ]; then + RMTFS_MODULE_LOAD_ATTEMPTED=1 + RMTFS_MODULE_LOAD_FAILED=1 + log_fail "RMTFS DT hardware is present but the configured $RMTFS_MODULE_NAME module file was not found" + else + log_info "No loadable $RMTFS_MODULE_NAME module was found, checking the active shared-memory backend" + fi + fi + + if [ "$RMTFS_MODULE_LOADED_BY_TEST" -eq 1 ]; then + rmtfs_wait_for_mainline_device 5 || true + fi + + rmtfs_record_runtime_activation + + RMTFS_MAINLINE_DEVICE=$(rmtfs_find_mainline_device 2>/dev/null || true) + RMTFS_UIO_DEVICE=$(rmtfs_find_uio_device 2>/dev/null || true) + + if [ "$RMTFS_MODULE_LOADED_BY_TEST" -eq 1 ] && \ + [ -z "$RMTFS_MAINLINE_DEVICE" ]; then + log_warn "The RMTFS module loaded, but no /dev/qcom_rmtfs_mem device appeared" + fi + + RMTFS_RUNTIME_PREPARED=1 + return 0 +} + +# Start the image-provided RMTFS service when it is not already running. +# Cleanup restores the initial service state. +rmtfs_start_service_if_available() { + if rmtfs_process_running rmtfs; then + rmtfs_record_runtime_activation + return 0 + fi + + rmtfs_wait_for_process rmtfs 3 || true + if rmtfs_process_running rmtfs; then + rmtfs_record_runtime_activation + return 0 + fi + + if [ -n "${RMTFS_MAINLINE_DEVICE:-}" ]; then + rmtfs_service_candidates="rmtfs rmtfs-dir" + else + rmtfs_service_candidates="rmtfs-dir rmtfs" + fi + rmtfs_systemd_service_found=0 + + for rmtfs_service_candidate in $rmtfs_service_candidates; do + if ! systemd_service_exists "$rmtfs_service_candidate"; then + continue + fi + + rmtfs_systemd_service_found=1 + log_info "Starting image-provided RMTFS service: $rmtfs_service_candidate" + RMTFS_RUNTIME_ACTIVATION_EXPECTED=1 + if systemd_service_start_safe "$rmtfs_service_candidate"; then + if ! rmtfs_service_was_active "$rmtfs_service_candidate"; then + RMTFS_SYSTEMD_STARTED_BY_TEST="$rmtfs_service_candidate" + fi + + if rmtfs_wait_for_process rmtfs 5; then + rmtfs_record_runtime_activation + log_pass "RMTFS service started for functional validation: $rmtfs_service_candidate" + return 0 + fi + fi + + if systemd_service_is_active "$rmtfs_service_candidate" && \ + ! rmtfs_service_was_active "$rmtfs_service_candidate"; then + RMTFS_SYSTEMD_STARTED_BY_TEST="$rmtfs_service_candidate" + fi + log_warn "RMTFS service did not become active: $rmtfs_service_candidate" + + if [ "${RMTFS_SYSTEMD_STARTED_BY_TEST:-}" = "$rmtfs_service_candidate" ]; then + log_info "Stopping unsuccessful RMTFS service attempt: $rmtfs_service_candidate" + if ! systemd_service_stop_safe "$rmtfs_service_candidate"; then + log_warn "Unable to stop unsuccessful RMTFS service attempt: $rmtfs_service_candidate" + return 1 + fi + RMTFS_SYSTEMD_STARTED_BY_TEST="" + fi + done + + rmtfs_init_script="/etc/init.d/rmtfs" + if [ -x "$rmtfs_init_script" ]; then + log_info "Starting image-provided RMTFS SysV service: $rmtfs_init_script" + RMTFS_RUNTIME_ACTIVATION_EXPECTED=1 + if "$rmtfs_init_script" start >/dev/null 2>&1; then + RMTFS_SYSV_STARTED_BY_TEST="$rmtfs_init_script" + + if rmtfs_wait_for_process rmtfs 5; then + rmtfs_record_runtime_activation + log_pass "RMTFS SysV service started for functional validation: $rmtfs_init_script" + return 0 + fi + fi + + log_warn "RMTFS SysV service did not become active: $rmtfs_init_script" + return 1 + fi + + if [ "$rmtfs_systemd_service_found" -eq 0 ]; then + log_info "No image-provided RMTFS service definition was found" + else + log_warn "No image-provided RMTFS service started successfully" + fi + return 1 +} + +rmtfs_daemon_cmdline_readable() { + rmtfs_daemon_pid=$(get_pid rmtfs 2>/dev/null || true) + if [ -z "$rmtfs_daemon_pid" ] || \ + [ ! -r "/proc/$rmtfs_daemon_pid/cmdline" ]; then + return 1 + fi + + return 0 +} + +rmtfs_daemon_uses_partitions() { + rmtfs_daemon_pid=$(get_pid rmtfs 2>/dev/null || true) + if [ -z "$rmtfs_daemon_pid" ] || \ + [ ! -r "/proc/$rmtfs_daemon_pid/cmdline" ]; then + return 1 + fi + + tr '\0' '\n' < "/proc/$rmtfs_daemon_pid/cmdline" 2>/dev/null | + grep -qE -- '^-[^-]*P' +} + +# Restore service and module state captured by rmtfs_runtime_prepare(). +rmtfs_runtime_cleanup() { + rmtfs_cleanup_rc=0 + + if [ "${RMTFS_RUNTIME_PREPARED:-0}" -ne 1 ]; then + return 0 + fi + + if [ "${RMTFS_DAEMON_WAS_RUNNING:-0}" -eq 0 ]; then + rmtfs_record_runtime_activation + + if [ -n "${RMTFS_SYSTEMD_STARTED_BY_TEST:-}" ] && \ + systemd_service_is_active "$RMTFS_SYSTEMD_STARTED_BY_TEST"; then + log_info "Restoring RMTFS service snapshot by stopping $RMTFS_SYSTEMD_STARTED_BY_TEST" + if ! systemd_service_stop_safe "$RMTFS_SYSTEMD_STARTED_BY_TEST"; then + log_warn "Unable to stop RMTFS service activated during the test: $RMTFS_SYSTEMD_STARTED_BY_TEST" + rmtfs_cleanup_rc=1 + fi + fi + + if [ -n "${RMTFS_SYSV_STARTED_BY_TEST:-}" ]; then + log_info "Restoring RMTFS SysV service snapshot" + if ! "$RMTFS_SYSV_STARTED_BY_TEST" stop >/dev/null 2>&1; then + log_warn "Unable to stop RMTFS SysV service started during the test" + rmtfs_cleanup_rc=1 + fi + RMTFS_SYSV_STARTED_BY_TEST="" + fi + + if rmtfs_process_running rmtfs && \ + [ "${RMTFS_DAEMON_STARTED_BY_TEST:-0}" -eq 1 ]; then + rmtfs_started_pid=$(get_pid rmtfs 2>/dev/null || true) + if [ -n "$rmtfs_started_pid" ]; then + log_info "Stopping the rmtfs process that appeared during the test: PID $rmtfs_started_pid" + if ! kill_process "$rmtfs_started_pid"; then + rmtfs_cleanup_rc=1 + fi + fi + fi + + if ! rmtfs_wait_for_process_exit rmtfs 5; then + log_warn "The rmtfs daemon is still running after service cleanup" + rmtfs_cleanup_rc=1 + fi + fi + + if [ "${RMTFS_MODULE_LOADED_BY_TEST:-0}" -eq 1 ]; then + log_info "Removing RMTFS module loaded by this test: $RMTFS_MODULE_NAME" + if unload_kernel_module "$RMTFS_MODULE_NAME" false; then + RMTFS_MODULE_LOADED_BY_TEST=0 + log_pass "RMTFS module state restored to initially unloaded" + else + log_fail "Unable to remove RMTFS module loaded by this test" + rmtfs_cleanup_rc=1 + fi + else + log_info "RMTFS module cleanup: module was not loaded by this test" + fi + + return "$rmtfs_cleanup_rc" +} + # Detects and returns the first available media node (e.g., /dev/media0). # Returns failure if no media nodes are present. detect_media_node() {