From 3b69ffc513776d63390e4678e255e3a780072a07 Mon Sep 17 00:00:00 2001 From: Anand Kulkarni Date: Thu, 17 Sep 2026 12:55:58 +0530 Subject: [PATCH 1/2] fastrpc-test: Replace SoC filters with runtime domain capability validation Remove hardcoded SoC-based domain filtering and per-SoC PD support maps. Use fastrpc-healthcheck as the primary capability source for domain online state and signed/unsigned PD support. Fall back to remoteproc/endpoint discovery for domains and a conservative protocol map for PD support when healthcheck is unavailable. Enables previously blacklisted platforms and derives PD support from live hardware capability. Add --healthcheck-timeout, enforce finite --timeout (default 120s), log domain_source and pd_source in [FASTRPC-POLICY], and discover DSP skeletons from actual .so presence rather than hardcoded ABI directories. Signed-off-by: Anand Kulkarni --- .../CDSP/fastrpc_test/fastrpc_test.yaml | 12 +- .../CDSP/fastrpc_test/fastrpc_test_README.md | 191 +++-- .../Multimedia/CDSP/fastrpc_test/run.sh | 311 ++++--- Runner/utils/lib_fastrpc.sh | 782 ++++++++++++++++-- 4 files changed, 1027 insertions(+), 269 deletions(-) mode change 100755 => 100644 Runner/suites/Multimedia/CDSP/fastrpc_test/run.sh diff --git a/Runner/suites/Multimedia/CDSP/fastrpc_test/fastrpc_test.yaml b/Runner/suites/Multimedia/CDSP/fastrpc_test/fastrpc_test.yaml index 0cae5cf9b..ec5dd3ae5 100644 --- a/Runner/suites/Multimedia/CDSP/fastrpc_test/fastrpc_test.yaml +++ b/Runner/suites/Multimedia/CDSP/fastrpc_test/fastrpc_test.yaml @@ -5,10 +5,9 @@ metadata: The **fastrpc_test** runner validates FastRPC (Fast Remote Procedure Call) on Qualcomm targets. **Enhanced Test Coverage**: - - Now tests ALL supported DSP domains detected at runtime (ADSP, MDSP, SDSP, CDSP, CDSP1, GPDSP0, GPDSP1) - - Tests BOTH signed (system) and unsigned (user) Protection Domains where hardware supports them - - ADSP/MDSP/SDSP: signed PD only; CDSP/CDSP1/GPDSP: both signed and unsigned PDs - - QCS9075, QCS8275, QCS8300, QCS9100: GPDSP0/GPDSP1 domains are skipped (fastrpc_tests binaries not supported temporarily) + - Uses fastrpc-healthcheck when available to discover online, FastRPC-capable DSP domains + - Derives signed and unsigned Protection Domain support from the live healthcheck report + - Without healthcheck, discovers domains from remoteproc plus FastRPC endpoints and uses a conservative protocol map for PD support - For single-domain testing: set DOMAIN_MODE="single" and specify DOMAIN or DOMAIN_NAME os: - linux @@ -24,7 +23,8 @@ params: PD_MODE: "both" # both|signed-only|unsigned-only (default: both - tests both PDs where supported) UNSIGNED_PD: "" # If set to non-zero, runs only unsigned PD mode (-U 1) REPEAT: 1 # Number of repetitions (default: 1) - TIMEOUT: "" # Timeout for each run (no timeout if omitted) + TIMEOUT: 120 # Required positive timeout for each fastrpc_test invocation + HEALTHCHECK_TIMEOUT: 15 # Required positive timeout for fastrpc-healthcheck run: steps: @@ -32,5 +32,5 @@ run: - cd Runner/suites/Multimedia/CDSP/fastrpc_test - UNSIGNED_PD_PARAM="" - if [ -n "${UNSIGNED_PD}" ] && [ "${UNSIGNED_PD}" != 0 ]; then UNSIGNED_PD_PARAM="--unsigned-pd"; fi - - ./run.sh --arch "${ARCH}" --bin-dir "${BIN_DIR}" --domain-mode "${DOMAIN_MODE}" --domain "${DOMAIN}" --domain-name "${DOMAIN_NAME}" --pd-mode "${PD_MODE}" --repeat "${REPEAT}" --timeout "${TIMEOUT}" $UNSIGNED_PD_PARAM || true + - ./run.sh --arch "${ARCH}" --bin-dir "${BIN_DIR}" --domain-mode "${DOMAIN_MODE}" --domain "${DOMAIN}" --domain-name "${DOMAIN_NAME}" --pd-mode "${PD_MODE}" --repeat "${REPEAT}" --timeout "${TIMEOUT}" --healthcheck-timeout "${HEALTHCHECK_TIMEOUT}" $UNSIGNED_PD_PARAM || true - $REPO_PATH/Runner/utils/send-to-lava.sh fastrpc_test.res || true diff --git a/Runner/suites/Multimedia/CDSP/fastrpc_test/fastrpc_test_README.md b/Runner/suites/Multimedia/CDSP/fastrpc_test/fastrpc_test_README.md index 85f22b00f..2410be81a 100644 --- a/Runner/suites/Multimedia/CDSP/fastrpc_test/fastrpc_test_README.md +++ b/Runner/suites/Multimedia/CDSP/fastrpc_test/fastrpc_test_README.md @@ -1,33 +1,44 @@ -# FastRPC Test Script for Qualcomm Linux-based Platforms (Yocto) +# FastRPC Test Script for Qualcomm Linux-based Platforms ## Overview -The **fastrpc_test** runner validates FastRPC (Fast Remote Procedure Call) on Qualcomm targets, offloading work to DSP domains (e.g., **CDSP**). -It wraps the public [fastrpc test application](https://github.com/quic/fastrpc) with **robust logging, parameter control, and CI-friendly output**. +The **fastrpc_test** runner validates FastRPC (Fast Remote Procedure Call) on Qualcomm targets, +offloading work to all supported DSP domains (ADSP, MDSP, SDSP, CDSP, CDSP1, GDSP0, GDSP1). +It wraps the public [fastrpc test application](https://github.com/quic/fastrpc) with **robust +logging, parameter control, and CI-friendly output**. Supported capabilities: -- Auto-detect architecture from SoC ID. -- Multiple iterations and optional timeouts. -- Precise control over where the binary and assets live via `--bin-dir` and `--assets-dir`. -- Unbuffered output via `stdbuf` or `script` when available (falls back gracefully). +- Uses `fastrpc-healthcheck` when available to discover online DSPs, FastRPC support, signed and + unsigned PD support, firmware information, DSP library paths, and DMA-BUF heap readiness. +- When healthcheck is unavailable, discovers runnable domains from remoteproc state and FastRPC + character endpoints. The fallback cannot discover PD support directly, so it uses the documented + conservative protocol map. Host, test, and DSP skeleton libraries remain runtime-discovered. +- Multiple iterations with a required finite timeout. +- Precise control over binary location via `--bin-dir`. +- Line-buffered output through `stdbuf` when available. ## Features - **Calculator**, **HAP**, and **Multithreading** examples (as provided by `fastrpc_test`) -- CI-ready logs with timestamps and per-iteration results -- Parameterized control (`--arch`, `--repeat`, `--timeout`, `--bin-dir`, `--assets-dir`, `--verbose`) -- Auto-detection fallback for binary and assets -- Silent directory scan (no noisy `ls` dumps) +- CI-ready logs with timestamps and per-iteration, per-domain/PD results +- Parameterized control (`--arch`, `--repeat`, `--timeout`, `--bin-dir`, `--domain-mode`, + `--domain`, `--domain-name`, `--pd-mode`, `--unsigned-pd`, `--verbose`) +- Auto-discovery of system libraries and DSP skeletons for both Yocto and Debian layouts +- Runtime domain and PD discovery without SoC-name filtering ## Prerequisites Have these on the target (or specify paths with the flags below): - `fastrpc_test` binary (from [github.com/quic/fastrpc](https://github.com/quic/fastrpc)) -- A **parent directory** that contains a `linux/` subfolder with the required libraries (often alongside the binary), and architecture folders such as `v68`, `v73`, `v75`. +- Optional `fastrpc-healthcheck`. When installed, this is the primary distro-independent + capability source. +- FastRPC system libraries and DSP skeletons auto-discovered from standard locations: + - Yocto: `/usr/local/lib`, `/usr/local/lib/fastrpc_test`, `/usr/local/share/fastrpc_test` + - Debian: `/usr/lib/`, `/usr/lib//fastrpc_test`, `/usr/share/fastrpc_test` + - RPM-based images: `/usr/lib64`, `/usr/lib64/fastrpc_test`, `/usr/share/fastrpc_test` - Optional but recommended: - - `stdbuf` **or** `script` (for unbuffered stdout/stderr) - - `timeout` (GNU coreutils) for wall-clock limiting; the script provides a portable fallback if missing. + - `stdbuf` for line-buffered output. Execution remains bounded without it. ## Directory Structure @@ -38,7 +49,8 @@ Runner/ │ │ ├── CDSP/ │ │ │ ├── fastrpc_test/ │ │ │ │ ├── run.sh -│ │ │ │ ├── fastrpc_test_README.md +│ │ │ │ ├── fastrpc_test_README.md +│ │ │ │ ├── fastrpc_test.yaml ``` ## Usage @@ -49,69 +61,66 @@ Runner/ Usage: run.sh [OPTIONS] Options: - --arch Architecture (only if explicitly provided) - --bin-dir Directory containing 'fastrpc_test' (default: /usr/bin) - --assets-dir Directory that CONTAINS 'linux/' (info only; we run from the binary dir) - --user-pd Use '-U 1' (user/unsigned PD). Default is '-U 0'. - --repeat Number of repetitions (default: 1) - --timeout Timeout for each run (no timeout if omitted) - --verbose Extra logging for CI debugging - --help Show this help + --arch Architecture (only if explicitly provided) + --bin-dir Directory containing 'fastrpc_test' (default: /usr/bin) + --domain <0|1|2|3|4|5|6> DSP domain: 0=ADSP 1=MDSP 2=SDSP 3=CDSP 4=CDSP1 5=GDSP0 6=GDSP1 + --domain-name DSP domain: adsp|mdsp|sdsp|cdsp|cdsp1|gdsp0|gdsp1 + --domain-mode Discover all domains or run only one (default: all-supported) + --pd-mode Select PD mode(s) to run (default: both) + --unsigned-pd Use '-U 1' (user/unsigned PD). Overrides --pd-mode. + --repeat Number of repetitions (default: 1) + --timeout Timeout for each run (default: 120, must be greater than zero) + --healthcheck-timeout Timeout for fastrpc-healthcheck (default: 15) + --verbose Extra logging for CI debugging + --help Show this help Env: - FASTRPC_USER_PD=0|1 Sets PD (-U value). CLI --user-pd overrides to 1. - FASTRPC_EXTRA_FLAGS Extra flags appended to the command. - ALLOW_BIN_FASTRPC=1 Permit using /bin/fastrpc_test (otherwise refused). - -The test executes FROM the assets directory so 'fastrpc_test' can find deps. + FASTRPC_DOMAIN=0|1|2|3|4|5|6 Sets domain; CLI --domain/--domain-name wins. + FASTRPC_DOMAIN_NAME=adsp|... Named domain; CLI wins. + FASTRPC_UNSIGNED_PD=0|1 Sets PD (-U value). CLI --unsigned-pd overrides to 1. + FASTRPC_EXTRA_FLAGS Extra flags appended (space-separated). + FASTRPC_HEALTHCHECK_BIN Optional path to fastrpc-healthcheck. + ALLOW_BIN_FASTRPC=1 Permit using /bin/fastrpc_test when --bin-dir=/bin. ``` ### Quick start ```bash -# If fastrpc_test is already in PATH and assets are discoverable: +# Default: dynamically discover runnable domains and supported PD modes +./run.sh + +# With repeat and timeout: ./run.sh --repeat 3 --timeout 60 ``` ### Common scenarios ```bash -# Default expects /usr/bin/fastrpc_test and /usr/bin/linux -./run.sh +# 1) Use a custom binary directory +./run.sh --bin-dir /tmp/stage/usr/bin -Common scenarios +# 2) Run only unsigned (user) PD across all domains +./run.sh --pd-mode unsigned-only -# 1) Use a custom binary directory (we will cd there and run ./fastrpc_test) -./run.sh --bin-dir /tmp/stage/usr/bin +# 3) Run only signed PD +./run.sh --pd-mode signed-only -# 2) Opt into user/unsigned PD (-U 1) -./run.sh --user-pd -# or via env -FASTRPC_USER_PD=1 ./run.sh +# 4) Force a specific domain (CDSP) +./run.sh --domain 3 +# or by name: +./run.sh --domain-name cdsp -# 3) Add extra flags (kept intact; -U is appended last as '-U 0/1') -FASTRPC_EXTRA_FLAGS="-d 3" ./run.sh +# 5) Force GDSP0 with unsigned PD +./run.sh --domain-name gdsp0 --pd-mode unsigned-only -# 4) Allow /bin explicitly (generally discouraged unless required) -ALLOW_BIN_FASTRPC=1 ./run.sh --bin-dir /bin +# 6) Run SDSP via environment variable with unsigned PD +FASTRPC_DOMAIN=2 FASTRPC_UNSIGNED_PD=1 ./run.sh -# 5) Run multiple iterations with a timeout and verbose logs +# 7) Run multiple iterations with verbose logs ./run.sh --repeat 3 --timeout 120 --verbose -Force CDSP explicitly: - -# 6) ./run.sh --domain 3 -# or -# 6) ./run.sh --domain-name cdsp - -Use ADSP and user PD: - -# 7) ./run.sh --domain-name adsp --user-pd - -From env (CI): - -FASTRPC_DOMAIN=2 FASTRPC_USER_PD=1 ./run.sh -# => SDSP with -U 1 +# 8) Allow /bin explicitly (generally discouraged) +ALLOW_BIN_FASTRPC=1 ./run.sh --bin-dir /bin ``` ### LAVA integration example @@ -125,34 +134,60 @@ FASTRPC_DOMAIN=2 FASTRPC_USER_PD=1 ./run.sh ``` [INFO] 2025-09-02 10:44:46 - -------------------Starting fastrpc_test Testcase---------------------------- -[INFO] 2025-09-02 10:44:46 - Using binary: /usr/bin/fastrpc_test -[INFO] 2025-09-02 10:44:46 - PD setting: -U 0 (use --user-pd to set -U 1) -[INFO] 2025-09-02 10:44:46 - Run dir: /usr/bin (launching ./fastrpc_test) -[INFO] 2025-09-02 10:44:46 - Executing: ./fastrpc_test -d 3 -t linux -U 0 ------ iter1 output begin ----- +[INFO] 2025-09-02 10:44:46 - Domain mode: all-supported +[INFO] 2025-09-02 10:44:46 - Domains to test: 0 3 +[INFO] 2025-09-02 10:44:46 - PD mode: both +[INFO] 2025-09-02 10:44:46 - Running ADSP_signed_iter1 | domain=ADSP | pd=signed +[INFO] 2025-09-02 10:44:46 - Executing: ./fastrpc_test -d 0 -t linux -U 0 +----- ADSP_signed_iter1 output begin ----- ... fastrpc_test output ... ------ iter1 output end ----- -[PASS] 2025-09-02 10:44:50 - iter1: success -[PASS] 2025-09-02 10:44:50 - fastrpc_test : Test Passed (1/1) +----- ADSP_signed_iter1 output end ----- +[PASS] 2025-09-02 10:44:50 - ADSP_signed_iter1: success +... +[INFO] ================================================================================ +[INFO] FastRPC Test Summary +[INFO] ================================================================================ +[INFO] Domain | PD Mode | Total | Pass | Fail | Skip | Status +[INFO] -------------------------------------------------------------------------------- +[INFO] ADSP | Signed | 5 | 5 | 0 | 0 | PASS +[INFO] CDSP | Signed | 5 | 5 | 0 | 0 | PASS +[INFO] CDSP | Unsigned | 5 | 5 | 0 | 0 | PASS +[PASS] 2025-09-02 10:44:50 - fastrpc_test : Test Passed (3/3) ``` ## CI debugging aids -- Binary resolved to /bin/fastrpc_test: By default this is blocked to avoid loader/ramdisk mismatches. Set ALLOW_BIN_FASTRPC=1 and/or --bin-dir /bin if you intentionally need it. -- Error resolving path .../linux: Ensure linux/ is next to the binary (e.g., /usr/bin/linux). The script runs from the binary dir specifically to make this work. -- Session create errors with -U 1: If you opt into user/unsigned PD and see 0x80000416, confirm your image includes unsigned shells/policies (or revert to the default -U 0). -- Per-iteration logs: `logs_fastrpc_test_/iterN.out` (+ `iterN.rc`) -- Summary result file: `fastrpc_test.res` (`PASS` / `FAIL`) -- Verbose mode: adds environment, resolutions, and timing details -- Graceful fallbacks when `stdbuf`, `script`, or `timeout` are missing -- Silent scan (no directory spam) during auto-detection + +- **Capability source**: The startup log reports `capability_source`, `domain_source`, and + `pd_source`. Raw and normalized healthcheck output is retained in the run log directory. +- **Offline automatic domain**: Logged and excluded from automatic selection. Other runnable + domains continue. +- **Offline explicitly requested domain**: Fails with the healthcheck or remoteproc state. +- **Missing runtime artifacts**: Skips before executing `fastrpc_test` and prints the missing + system library, test library, or skeleton evidence. +- **Binary resolved to /bin/fastrpc_test**: Blocked by default. Set `ALLOW_BIN_FASTRPC=1` or + use `--bin-dir` to a non-`/bin` path. +- **Session create errors with -U 1**: If unsigned PD returns `0x80000416`, confirm your image + includes unsigned shells/policies (or use `--pd-mode signed-only`). +- **Domain not discovered**: Check `dmesg` for remoteproc firmware load errors. The test + requires the DSP remoteproc to be registered and its firmware present in DT. +- **Per-iteration logs**: `logs_fastrpc_test_/__iter.out` (+ `.rc`, `.env`, `.cmd`) +- **Summary result file**: `fastrpc_test.res` (`PASS` / `FAIL` / `SKIP`) +- **Verbose mode**: adds environment, library resolution, and timing details ## Notes -- If `--arch` is omitted, the script maps `/sys/devices/soc0/soc_id` to a known arch (defaulting to `v68` when unknown). -- If `fastrpc_test` isn’t in `PATH`, use `--bin-dir` or add it to `PATH`. -- If you see `Error resolving path .../linux: No such file or directory`, point `--assets-dir` to the **parent** directory that actually contains a `linux/` subfolder. -- The script changes working directory to the resolved **assets** dir before invoking `fastrpc_test`, which is required for the binary to locate its shared libs/skeletons. +- Domain and PD support are derived from `fastrpc-healthcheck` when available. The fallback uses + runtime remoteproc and endpoint evidence, with a conservative protocol mapping for PD support. +- This suite runs the public `fastrpc_test` character-device path and therefore requires either + `/dev/fastrpc-` or `/dev/fastrpc--secure` for every selected domain. +- DSP skeleton directories are discovered dynamically by locating `.so` artifacts instead of + assuming fixed ABI directory names such as `v68` or `v75`. +- Override library discovery with `FASTRPC_LIB_SYS_DIR`, `FASTRPC_LIB_TEST_DIR`, or + `FASTRPC_SKEL_BASE`. CLI domain selection takes precedence over environment selection, which + takes precedence over runtime discovery. +- If `fastrpc_test` is not in the default path, use `--bin-dir` to specify its location. ## License -SPDX-License-Identifier: BSD-3-ClauseCopyright (c) Qualcomm Technologies, Inc. +SPDX-License-Identifier: BSD-3-Clause +Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. diff --git a/Runner/suites/Multimedia/CDSP/fastrpc_test/run.sh b/Runner/suites/Multimedia/CDSP/fastrpc_test/run.sh old mode 100755 new mode 100644 index d39789957..31a4d425f --- a/Runner/suites/Multimedia/CDSP/fastrpc_test/run.sh +++ b/Runner/suites/Multimedia/CDSP/fastrpc_test/run.sh @@ -36,6 +36,9 @@ fi # shellcheck disable=SC1090,SC1091 . "$TOOLS/lib_fastrpc.sh" + +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/lib_diag.sh" # Optional generic package-set recovery. # This must be a clean no-op when no package-set mapping exists for the active OS/provider. @@ -52,7 +55,8 @@ fi # Defaults REPEAT=1 -TIMEOUT="" +TIMEOUT=120 +HEALTHCHECK_TIMEOUT=15 ARCH="" BIN_DIR="" # directory that CONTAINS fastrpc_test ASSETS_DIR="" # kept for compatibility/logging (not used by new layout) @@ -63,13 +67,17 @@ CLI_DOMAIN_NAME="" DOMAIN_MODE="all-supported" # Default: test all supported domains PD_MODE="both" # Default: test both PDs where supported +# usage +# Print the supported FastRPC test CLI, environment overrides, and selection policy. +# Inputs: none. Output: help text on stdout. +# Returns: 0. Side effects: none. usage() { cat < @@ -77,13 +85,14 @@ Options: --arch Architecture (only if explicitly provided) --bin-dir Directory containing 'fastrpc_test' (default: /usr/bin) --assets-dir (compat) previously used when assets lived under 'linux/' - --domain <0|1|2|3|4|5|6> DSP domain: 0=ADSP, 1=MDSP, 2=SDSP, 3=CDSP, 4=CDSP1, 5=GPDSP0, 6=GPDSP1 - --domain-name DSP domain by name: adsp|mdsp|sdsp|cdsp|cdsp1|gpdsp0|gpdsp1 + --domain <0|1|2|3|4|5|6> DSP domain: 0=ADSP, 1=MDSP, 2=SDSP, 3=CDSP, 4=CDSP1, 5=GDSP0, 6=GDSP1 + --domain-name DSP domain by name: adsp|mdsp|sdsp|cdsp|cdsp1|gdsp0|gdsp1 --domain-mode Discover all supported domains or run only one (default: all-supported) --pd-mode Select PD mode(s) to run (default: both) --unsigned-pd Use '-U 1' (user/unsigned PD). Overrides --pd-mode for compatibility --repeat Number of repetitions (default: 1) - --timeout Timeout for each run (no timeout if omitted) + --timeout Timeout for each run (default: 120, must be greater than zero) + --healthcheck-timeout Timeout for fastrpc-healthcheck (default: 15) --verbose Extra logging for CI debugging --help Show this help @@ -99,6 +108,7 @@ Env: FASTRPC_DOMAIN_NAME=adsp|... Named domain; CLI wins. FASTRPC_UNSIGNED_PD=0|1 Sets PD (-U value). CLI --unsigned-pd overrides to 1. FASTRPC_EXTRA_FLAGS Extra flags appended (space-separated). + FASTRPC_HEALTHCHECK_BIN Optional path to fastrpc-healthcheck. ALLOW_BIN_FASTRPC=1 Permit using /bin/fastrpc_test when --bin-dir=/bin. Notes: @@ -113,33 +123,91 @@ Notes: /usr/lib/ /usr/lib//fastrpc_test /usr/share/fastrpc_test + RPM-based: + /usr/lib64 + /usr/lib64/fastrpc_test + /usr/share/fastrpc_test - Optional overrides: FASTRPC_LIB_SYS_DIR FASTRPC_LIB_TEST_DIR FASTRPC_SKEL_BASE -- Domain mapping: ADSP=0 MDSP=1 SDSP=2 CDSP=3 CDSP1=4 GPDSP0=5 GPDSP1=6 -- PD support: ADSP/MDSP/SDSP support signed only; CDSP/CDSP1/GPDSP support both. +- Domain mapping: ADSP=0 MDSP=1 SDSP=2 CDSP=3 CDSP1=4 GDSP0=5 GDSP1=6 +- With healthcheck, domain and PD support come from its live capability report. +- Without healthcheck, domains come from remoteproc plus endpoint discovery, while PD support uses a conservative protocol fallback map. EOF } -# --------------------- Parse arguments ------------------------- -while [ $# -gt 0 ]; do - case "$1" in - --arch) ARCH="$2"; shift 2 ;; - --bin-dir) BIN_DIR="$2"; shift 2 ;; - --assets-dir) ASSETS_DIR="$2"; shift 2 ;; - --domain) CLI_DOMAIN="$2"; shift 2 ;; - --domain-name) CLI_DOMAIN_NAME="$2"; shift 2 ;; - --domain-mode) DOMAIN_MODE="$2"; shift 2 ;; - --pd-mode) PD_MODE="$2"; shift 2 ;; - --unsigned-pd) UNSIGNED_PD_FLAG=1; shift ;; - --repeat) REPEAT="$2"; shift 2 ;; - --timeout) TIMEOUT="$2"; shift 2 ;; - --verbose) VERBOSE=1; shift ;; - --help) usage; exit 0 ;; - *) echo "[ERROR] Unknown argument: $1" >&2; usage; echo "$TESTNAME : FAIL" >"$RESULT_FILE"; exit 0 ;; - esac -done +# parse_args ARG... +# Parse command-line policy and compatibility options into suite configuration globals. +# Inputs: the original command-line argument vector. Output: no stdout contract. +# Returns: 0 for valid input, and exits the suite after help or invalid input. +# Side effects: updates option globals and writes a FAIL result for unknown arguments. +parse_args() { + while [ "$#" -gt 0 ]; do + case "$1" in + --arch) + ARCH="$2" + shift 2 + ;; + --bin-dir) + BIN_DIR="$2" + shift 2 + ;; + --assets-dir) + ASSETS_DIR="$2" + shift 2 + ;; + --domain) + CLI_DOMAIN="$2" + shift 2 + ;; + --domain-name) + CLI_DOMAIN_NAME="$2" + shift 2 + ;; + --domain-mode) + DOMAIN_MODE="$2" + shift 2 + ;; + --pd-mode) + PD_MODE="$2" + shift 2 + ;; + --unsigned-pd) + UNSIGNED_PD_FLAG=1 + shift + ;; + --repeat) + REPEAT="$2" + shift 2 + ;; + --timeout) + TIMEOUT="$2" + shift 2 + ;; + --healthcheck-timeout) + HEALTHCHECK_TIMEOUT="$2" + shift 2 + ;; + --verbose) + VERBOSE=1 + shift + ;; + --help) + usage + exit 0 + ;; + *) + echo "[ERROR] Unknown argument: $1" >&2 + usage + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 + ;; + esac + done +} + +parse_args "$@" # ---- Back-compat: accept --assets-dir but ignore in the new auto-discovered layout. # Export so external tooling (or legacy wrappers) can still read it. @@ -157,10 +225,27 @@ export UNSIGNED_PD_FLAG export VERBOSE # ---------- Validation ---------- -case "$REPEAT" in *[!0-9]*|"") log_error "Invalid --repeat: $REPEAT"; echo "$TESTNAME : FAIL" >"$RESULT_FILE"; exit 0 ;; esac -if [ -n "$TIMEOUT" ]; then - case "$TIMEOUT" in *[!0-9]*|"") log_error "Invalid --timeout: $TIMEOUT"; echo "$TESTNAME : FAIL" >"$RESULT_FILE"; exit 0 ;; esac -fi +case "$REPEAT" in + *[!0-9]*|""|0) + log_error "Invalid --repeat: $REPEAT" + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 + ;; +esac +case "$TIMEOUT" in + *[!0-9]*|""|0) + log_error "Invalid --timeout: $TIMEOUT" + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 + ;; +esac +case "$HEALTHCHECK_TIMEOUT" in + *[!0-9]*|""|0) + log_error "Invalid --healthcheck-timeout: $HEALTHCHECK_TIMEOUT" + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 + ;; +esac # Validate enhanced options case "$DOMAIN_MODE" in all-supported|single) : ;; *) log_error "Invalid --domain-mode: $DOMAIN_MODE"; echo "$TESTNAME : FAIL" >"$RESULT_FILE"; exit 0 ;; esac case "$PD_MODE" in both|signed-only|unsigned-only) : ;; *) log_error "Invalid --pd-mode: $PD_MODE"; echo "$TESTNAME : FAIL" >"$RESULT_FILE"; exit 0 ;; esac @@ -185,7 +270,6 @@ log_info "-------------------Starting $TESTNAME Testcase------------------------ log_info "Kernel: $(uname -a 2>/dev/null || echo N/A)" log_info "Date(UTC): $(date -u 2>/dev/null || echo N/A)" log_soc_info -SOC_MACHINE="$(tr -s ' ' < /sys/devices/soc0/machine 2>/dev/null | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" # -------------------- Binary directory resolution ----------------- if [ -n "$BIN_DIR" ]; then @@ -213,6 +297,50 @@ if [ ! -x "$RUN_BIN" ]; then exit 0 fi +# -------------------- Logging root ----------------------------- +TS="$(date +%Y%m%d-%H%M%S)" +LOG_ROOT="./logs_${TESTNAME}_${TS}" +if ! mkdir -p "$LOG_ROOT"; then + log_error "Cannot create $LOG_ROOT" + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 +fi + +log_info "Evidence directory: $test_path/$LOG_ROOT" + +# -------------------- Capability discovery --------------------- +FASTRPC_HEALTHCHECK_LOG="$LOG_ROOT/fastrpc_healthcheck.log" +FASTRPC_HEALTHCHECK_TSV="$LOG_ROOT/fastrpc_healthcheck.tsv" +export FASTRPC_HEALTHCHECK_TSV + +fastrpc_capture_healthcheck \ + "$FASTRPC_HEALTHCHECK_LOG" \ + "$HEALTHCHECK_TIMEOUT" +healthcheck_status=$? + +case "$healthcheck_status" in + 0) + if fastrpc_parse_healthcheck \ + "$FASTRPC_HEALTHCHECK_LOG" \ + "$FASTRPC_HEALTHCHECK_TSV"; then + export FASTRPC_HEALTHCHECK_DSP_LIB_DIR + log_info "[FASTRPC-POLICY] capability_source=fastrpc-healthcheck domain_source=healthcheck pd_source=healthcheck parsed=$FASTRPC_HEALTHCHECK_TSV" + fastrpc_log_healthcheck_records + else + rm -f "$FASTRPC_HEALTHCHECK_TSV" + log_warn "[FASTRPC-POLICY] capability_source=runtime-fallback domain_source=remoteproc-endpoint pd_source=protocol-fallback-map reason=healthcheck-output-unrecognized artifact=$FASTRPC_HEALTHCHECK_LOG" + fi + ;; + 1) + rm -f "$FASTRPC_HEALTHCHECK_TSV" + log_warn "[FASTRPC-POLICY] capability_source=runtime-fallback domain_source=remoteproc-endpoint pd_source=protocol-fallback-map reason=healthcheck-failed artifact=$FASTRPC_HEALTHCHECK_LOG" + ;; + 2) + rm -f "$FASTRPC_HEALTHCHECK_TSV" + log_info "[FASTRPC-POLICY] capability_source=runtime-fallback domain_source=remoteproc-endpoint pd_source=protocol-fallback-map reason=fastrpc-healthcheck-not-installed" + ;; +esac + # -------------------- Runtime layout discovery -------------------- fastrpc_setup_runtime_layout @@ -228,59 +356,54 @@ ensure_usr_lib_dsp_symlinks log_dsp_remoteproc_status # -------------------- Domain and PD selection ------------------- -# Resolve domains and PDs to test DOMAINS_TO_TEST="$(resolve_domains_to_test)" +SELECTION_SOURCE="auto" +if [ -n "$CLI_DOMAIN_NAME" ] || [ -n "$CLI_DOMAIN" ] || + [ "$DOMAIN_MODE" = "single" ]; then + SELECTION_SOURCE="explicit" +fi + +if [ -z "$DOMAINS_TO_TEST" ] && [ "$SELECTION_SOURCE" = "explicit" ]; then + log_fail "$TESTNAME FAIL - explicitly requested domain is invalid or unavailable" + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 +fi + if [ -z "$DOMAINS_TO_TEST" ]; then log_skip "$TESTNAME SKIP - no mapped/supported domains detected" echo "$TESTNAME : SKIP" >"$RESULT_FILE" exit 0 fi -# -------------------- SoC-specific domain blacklist -------------------- -# QRB2210: FastRPC not supported - skip entire test -# QCS9075, QCS8275, QCS8300, QCS9100: GPDSP0 (domain 5) and GPDSP1 (domain 6) not supported currently -# SM8850: libhap_example HAP_mem DMA not supported - treat as known skip per invocation -# -# Do not skip Glymur CRD by SoC name. Newer Glymur/Debian images expose -# ADSP/CDSP remoteproc instances and FastRPC skeletons, so runtime discovery -# should decide whether the test can run. -soc_skip_all=0 -soc_skip_gpdsp=0 - -case "$SOC_MACHINE" in - *QRB2210*|*"Glymur CRD"*) - soc_skip_all=1 - ;; - *QCS9075*|*QCS8275*|*QCS8300*|*QCS9100*) - soc_skip_gpdsp=1 - ;; -esac - -if [ "$soc_skip_all" -eq 1 ]; then - log_skip "$TESTNAME SKIP - SoC $SOC_MACHINE does not support FastRPC" +if ! fastrpc_validate_runtime_artifacts; then + log_skip "$TESTNAME SKIP - FastRPC test artifacts are incomplete: $FASTRPC_ARTIFACT_ERROR" echo "$TESTNAME : SKIP" >"$RESULT_FILE" exit 0 fi -if [ "$soc_skip_gpdsp" -eq 1 ]; then - filtered="" - for d in $DOMAINS_TO_TEST; do - case "$d" in - 5|6) log_info "SoC $SOC_MACHINE: skipping $(domain_to_name "$d") (not supported)" ;; - *) filtered="${filtered:+$filtered }$d" ;; - esac - done - DOMAINS_TO_TEST="$filtered" -fi +# Validate every selected domain before handing it to fastrpc_test. +for d in $DOMAINS_TO_TEST; do + fastrpc_domain_ready "$d" + domain_ready_status=$? -if [ -z "$DOMAINS_TO_TEST" ]; then - log_skip "$TESTNAME SKIP - no supported domains remain after SoC filter ($SOC_MACHINE)" - echo "$TESTNAME : SKIP" >"$RESULT_FILE" - exit 0 -fi + if [ "$domain_ready_status" -ne 0 ]; then + log_fail "$TESTNAME FAIL - $(domain_to_name "$d") is not ready, selection=$SELECTION_SOURCE" + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 + fi + + if ! fastrpc_domain_endpoint_available "$d"; then + label="$(domain_to_endpoint_label "$d")" + log_fail "$TESTNAME FAIL - $(domain_to_name "$d"): /dev/fastrpc-${label}[-secure] not found" + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 + fi + +done log_info "Domain mode: $DOMAIN_MODE" +log_info "Domain selection source: $SELECTION_SOURCE" log_info "Domains to test: $DOMAINS_TO_TEST" # Build human-readable domain names @@ -299,23 +422,23 @@ log_info "PD mode: $PD_MODE" # -------------------- Buffering tool availability --------------- HAVE_STDBUF=0; command -v stdbuf >/dev/null 2>&1 && HAVE_STDBUF=1 -HAVE_SCRIPT=0; command -v script >/dev/null 2>&1 && HAVE_SCRIPT=1 -HAVE_TIMEOUT=0; command -v timeout >/dev/null 2>&1 && HAVE_TIMEOUT=1 buf_label="none" if [ $HAVE_STDBUF -eq 1 ]; then buf_label="stdbuf -oL -eL" -elif [ $HAVE_SCRIPT -eq 1 ]; then - buf_label="script -q" fi -# -------------------- Logging root ----------------------------- -TS="$(date +%Y%m%d-%H%M%S)" -LOG_ROOT="./logs_${TESTNAME}_${TS}" -mkdir -p "$LOG_ROOT" || { log_error "Cannot create $LOG_ROOT"; echo "$TESTNAME : FAIL" >"$RESULT_FILE"; exit 0; } +log_info "Repeats: $REPEAT | Timeout: ${TIMEOUT}s | Buffering: $buf_label" -tmo_label="none"; [ -n "$TIMEOUT" ] && tmo_label="${TIMEOUT}s" -log_info "Repeats: $REPEAT | Timeout: $tmo_label | Buffering: $buf_label" +matrix_combinations=0 +for matrix_domain in $DOMAINS_TO_TEST; do + for matrix_pd in $(effective_pds_for_domain "$matrix_domain"); do + [ -n "$matrix_pd" ] && matrix_combinations=$((matrix_combinations + 1)) + done +done +matrix_invocations=$((matrix_combinations * REPEAT)) +matrix_timeout_budget=$((matrix_invocations * TIMEOUT)) +log_info "[FASTRPC-MATRIX] combinations=$matrix_combinations repeats=$REPEAT invocations=$matrix_invocations maximum_timeout_budget=${matrix_timeout_budget}s" # -------------------- Run loop --------------------------------- # Nested loop over domains and PDs @@ -359,7 +482,7 @@ for DOMAIN in $DOMAINS_TO_TEST; do iter_rc="$LOG_ROOT/${iter_tag}.rc" iter_cmd="$LOG_ROOT/${iter_tag}.cmd" iter_env="$LOG_ROOT/${iter_tag}.env" - iter_dmesg="$LOG_ROOT/${iter_tag}.dmesg" + iter_dmesg="$LOG_ROOT/${iter_tag}_kernel" iso_now="$(date -u +%Y-%m-%dT%H:%M:%SZ)" set -- -d "$DOMAIN" -t linux @@ -391,16 +514,13 @@ for DOMAIN in $DOMAINS_TO_TEST; do ( cd "$RUN_DIR" || exit 127 if [ $HAVE_STDBUF -eq 1 ]; then - runWithTimeoutIfSet stdbuf -oL -eL ./fastrpc_test "$@" - elif [ $HAVE_SCRIPT -eq 1 ]; then - cmd_str="./fastrpc_test$(cmd_to_string "$@")" - if [ -n "$TIMEOUT" ] && [ $HAVE_TIMEOUT -eq 1 ]; then - script -q -c "timeout $TIMEOUT $cmd_str" /dev/null - else - script -q -c "$cmd_str" /dev/null - fi + diag_run_with_timeout \ + "$TIMEOUT" \ + stdbuf -oL -eL ./fastrpc_test "$@" else - runWithTimeoutIfSet ./fastrpc_test "$@" + diag_run_with_timeout \ + "$TIMEOUT" \ + ./fastrpc_test "$@" fi ) >"$iter_log" 2>&1 rc=$? @@ -415,7 +535,11 @@ for DOMAIN in $DOMAINS_TO_TEST; do if [ "$rc" -ne 0 ]; then log_fail "$iter_tag: fastrpc_test exited $rc" - dmesg | tail -n 300 > "$iter_dmesg" 2>/dev/null + mkdir -p "$iter_dmesg" + scan_dmesg_errors \ + "$iter_dmesg" \ + "fastrpc|remoteproc|adsprpc|cdsprpc|sdsprpc" \ + "" || true log_dsp_remoteproc_status fi @@ -433,15 +557,10 @@ for DOMAIN in $DOMAINS_TO_TEST; do fi # Track invocation result immediately - # SM8850: libhap_example HAP_mem DMA handle not supported - treat as known skip if [ "$rc" -eq 0 ] && [ -r "$iter_log" ] && grep -F -q -e "All tests completed successfully" -e "All applicable tests PASSED" "$iter_log"; then PASS_COUNT=$((PASS_COUNT+1)) combo_pass=$((combo_pass+1)) log_pass "$iter_tag: success" - elif case "$SOC_MACHINE" in *SM8850*) true ;; *) false ;; esac && only_hap_example_failed "$iter_log"; then - PASS_COUNT=$((PASS_COUNT+1)) - combo_pass=$((combo_pass+1)) - log_pass "$iter_tag: success (libhap_example.so HAP_mem skipped on $SOC_MACHINE - DMA handle not supported)" else combo_fail=$((combo_fail+1)) log_warn "$iter_tag: success pattern not found" @@ -478,8 +597,8 @@ overall_subtests_pass=0 overall_subtests_fail=0 overall_subtests_skip=0 -# shellcheck disable=SC2034 # _pass_cnt/_fail_cnt consumed from tracker but not used in display -while IFS=: read -r domain pd_val _pass_cnt _fail_cnt combo_total_tests combo_pass_tests combo_fail_tests combo_skip_tests; do +# shellcheck disable=SC2034 # pass_cnt/fail_cnt consumed from tracker but not used in display +while IFS=: read -r domain pd_val pass_cnt fail_cnt combo_total_tests combo_pass_tests combo_fail_tests combo_skip_tests; do [ -z "$domain" ] && continue dom_name="$(domain_to_name "$domain")" diff --git a/Runner/utils/lib_fastrpc.sh b/Runner/utils/lib_fastrpc.sh index dc4700192..092686136 100755 --- a/Runner/utils/lib_fastrpc.sh +++ b/Runner/utils/lib_fastrpc.sh @@ -9,18 +9,22 @@ # Yocto/current: # libs : /usr/local/lib # test libs : /usr/local/lib/fastrpc_test -# skeletons : /usr/local/share/fastrpc_test/v75, v68 +# skeletons : dynamically discovered below /usr/local/share/fastrpc_test # # Debian: # libs : /usr/lib/ # test libs : /usr/lib//fastrpc_test -# skeletons : /usr/share/fastrpc_test/v75, v68 +# skeletons : dynamically discovered below /usr/share/fastrpc_test # # Optional environment overrides: # FASTRPC_LIB_SYS_DIR # FASTRPC_LIB_TEST_DIR # FASTRPC_SKEL_BASE +# fastrpc_append_word_unique CURRENT NEW +# Append NEW to the space-separated CURRENT list when it is non-empty and absent. +# Inputs: two strings. Output: the resulting list on stdout. +# Returns: 0. Side effects: none. fastrpc_append_word_unique() { current="$1" new="$2" @@ -44,6 +48,10 @@ fastrpc_append_word_unique() { fi } +# fastrpc_append_colon_dir CURRENT_PATH NEW_DIR +# Append an existing NEW_DIR to a colon-separated path without duplicates. +# Inputs: a path list and directory. Output: the resulting path on stdout. +# Returns: 0. Side effects: probes NEW_DIR with test -d only. fastrpc_append_colon_dir() { current_path="$1" new_dir="$2" @@ -72,13 +80,21 @@ fastrpc_append_colon_dir() { esac } -fastrpc_first_existing_word_dir() { +# fastrpc_first_dir_with_file CANDIDATE_DIRS FILE_PATTERN +# Find the first space-separated candidate directory containing a matching file. +# Inputs: directory list and find-compatible basename pattern. Output: directory on stdout. +# Returns: 0 when found, 1 otherwise. Side effects: none. +fastrpc_first_dir_with_file() { candidate_dirs="$1" + file_pattern="$2" for candidate_dir in $candidate_dirs; do [ -n "$candidate_dir" ] || continue - if [ -d "$candidate_dir" ]; then + if [ -d "$candidate_dir" ] && + find "$candidate_dir" -maxdepth 1 \( -type f -o -type l \) \ + -name "$file_pattern" -print -quit 2>/dev/null | + grep -q .; then printf '%s\n' "$candidate_dir" return 0 fi @@ -87,6 +103,82 @@ fastrpc_first_existing_word_dir() { return 1 } +# fastrpc_find_file_in_dirs CANDIDATE_DIRS FILE_PATTERN +# Find the first matching regular file or symlink directly below candidate directories. +# Inputs: space-separated directories and basename pattern. Output: file path on stdout. +# Returns: 0 when found, 1 otherwise. Side effects: none. +fastrpc_find_file_in_dirs() { + candidate_dirs="$1" + file_pattern="$2" + + for candidate_dir in $candidate_dirs; do + [ -d "$candidate_dir" ] || continue + + candidate_file="$( + find "$candidate_dir" -maxdepth 1 \( -type f -o -type l \) \ + -name "$file_pattern" -print -quit 2>/dev/null + )" + if [ -n "$candidate_file" ]; then + printf '%s\n' "$candidate_file" + return 0 + fi + done + + return 1 +} + +# fastrpc_find_file_in_trees CANDIDATE_DIRS FILE_PATTERN +# Recursively find the first matching regular file or symlink in candidate trees. +# Inputs: space-separated tree roots and basename pattern. Output: file path on stdout. +# Returns: 0 when found, 1 otherwise. Side effects: none. +fastrpc_find_file_in_trees() { + candidate_dirs="$1" + file_pattern="$2" + + for candidate_dir in $candidate_dirs; do + [ -d "$candidate_dir" ] || continue + + candidate_file="$( + find "$candidate_dir" \( -type f -o -type l \) \ + -name "$file_pattern" -print -quit 2>/dev/null + )" + if [ -n "$candidate_file" ]; then + printf '%s\n' "$candidate_file" + return 0 + fi + done + + return 1 +} + +# fastrpc_find_skeleton_dirs SKELETON_BASES +# Discover unique directories containing any required FastRPC DSP skeleton. +# Input: space-separated tree roots. Output: colon-separated directories on stdout. +# Returns: 0. Side effects: none. +fastrpc_find_skeleton_dirs() { + skeleton_bases="$1" + skeleton_dirs="" + + for skeleton_base in $skeleton_bases; do + [ -d "$skeleton_base" ] || continue + + while IFS= read -r skeleton_dir; do + [ -n "$skeleton_dir" ] || continue + skeleton_dirs="$(fastrpc_append_colon_dir "$skeleton_dirs" "$skeleton_dir")" + done </dev/null | sort -u) +EOF + done + + printf '%s\n' "$skeleton_dirs" +} + +# fastrpc_detect_multiarch_triplet +# Detect the target multiarch triplet from image tools or the running architecture. +# Inputs: none. Output: triplet or an empty line on stdout. +# Returns: 0. Side effects: executes read-only compiler, package, and uname probes. fastrpc_detect_multiarch_triplet() { triplet="" @@ -115,6 +207,11 @@ fastrpc_detect_multiarch_triplet() { printf '%s\n' "$triplet" } +# fastrpc_discover_runtime_layout +# Discover FastRPC system, test, and DSP skeleton library locations. +# Inputs: optional FASTRPC_* path overrides and healthcheck DSP path globals. +# Outputs: FASTRPC_*_CHECKED and FASTRPC_RESOLVED_* shell globals, no stdout contract. +# Returns: 0. Side effects: read-only filesystem traversal. fastrpc_discover_runtime_layout() { FASTRPC_MULTIARCH_TRIPLET="$(fastrpc_detect_multiarch_triplet)" @@ -134,7 +231,9 @@ fastrpc_discover_runtime_layout() { # Yocto/current layout. FASTRPC_LIB_SYS_DIRS_CHECKED="$(fastrpc_append_word_unique "$FASTRPC_LIB_SYS_DIRS_CHECKED" "/usr/local/lib")" + FASTRPC_LIB_SYS_DIRS_CHECKED="$(fastrpc_append_word_unique "$FASTRPC_LIB_SYS_DIRS_CHECKED" "/usr/local/lib64")" FASTRPC_LIB_TEST_DIRS_CHECKED="$(fastrpc_append_word_unique "$FASTRPC_LIB_TEST_DIRS_CHECKED" "/usr/local/lib/fastrpc_test")" + FASTRPC_LIB_TEST_DIRS_CHECKED="$(fastrpc_append_word_unique "$FASTRPC_LIB_TEST_DIRS_CHECKED" "/usr/local/lib64/fastrpc_test")" FASTRPC_SKEL_BASES_CHECKED="$(fastrpc_append_word_unique "$FASTRPC_SKEL_BASES_CHECKED" "/usr/local/share/fastrpc_test")" # Debian/multiarch layout. @@ -145,25 +244,162 @@ fastrpc_discover_runtime_layout() { # Generic fallbacks. FASTRPC_LIB_SYS_DIRS_CHECKED="$(fastrpc_append_word_unique "$FASTRPC_LIB_SYS_DIRS_CHECKED" "/usr/lib")" + FASTRPC_LIB_SYS_DIRS_CHECKED="$(fastrpc_append_word_unique "$FASTRPC_LIB_SYS_DIRS_CHECKED" "/usr/lib64")" FASTRPC_LIB_TEST_DIRS_CHECKED="$(fastrpc_append_word_unique "$FASTRPC_LIB_TEST_DIRS_CHECKED" "/usr/lib/fastrpc_test")" + FASTRPC_LIB_TEST_DIRS_CHECKED="$(fastrpc_append_word_unique "$FASTRPC_LIB_TEST_DIRS_CHECKED" "/usr/lib64/fastrpc_test")" FASTRPC_SKEL_BASES_CHECKED="$(fastrpc_append_word_unique "$FASTRPC_SKEL_BASES_CHECKED" "/usr/share/fastrpc_test")" - FASTRPC_RESOLVED_LIB_SYS_DIR="$(fastrpc_first_existing_word_dir "$FASTRPC_LIB_SYS_DIRS_CHECKED" || true)" - FASTRPC_RESOLVED_LIB_TEST_DIR="$(fastrpc_first_existing_word_dir "$FASTRPC_LIB_TEST_DIRS_CHECKED" || true)" - FASTRPC_RESOLVED_SKEL_BASE="$(fastrpc_first_existing_word_dir "$FASTRPC_SKEL_BASES_CHECKED" || true)" + FASTRPC_RESOLVED_LIB_SYS_DIR="$( + fastrpc_first_dir_with_file "$FASTRPC_LIB_SYS_DIRS_CHECKED" 'lib*dsprpc.so*' || true + )" + FASTRPC_RESOLVED_LIB_SYS_PATH="" + for library_dir in $FASTRPC_LIB_SYS_DIRS_CHECKED; do + if find "$library_dir" -maxdepth 1 \( -type f -o -type l \) \ + -name 'lib*dsprpc.so*' -print -quit 2>/dev/null | + grep -q .; then + FASTRPC_RESOLVED_LIB_SYS_PATH="$( + fastrpc_append_colon_dir "$FASTRPC_RESOLVED_LIB_SYS_PATH" "$library_dir" + )" + fi + done - FASTRPC_RESOLVED_SKEL_PATH="" - if [ -n "$FASTRPC_RESOLVED_SKEL_BASE" ]; then - FASTRPC_RESOLVED_SKEL_PATH="$(fastrpc_append_colon_dir "$FASTRPC_RESOLVED_SKEL_PATH" "$FASTRPC_RESOLVED_SKEL_BASE/v75")" - FASTRPC_RESOLVED_SKEL_PATH="$(fastrpc_append_colon_dir "$FASTRPC_RESOLVED_SKEL_PATH" "$FASTRPC_RESOLVED_SKEL_BASE/v68")" + FASTRPC_RESOLVED_LIB_TEST_PATH="" + for test_library_dir in $FASTRPC_LIB_TEST_DIRS_CHECKED; do + if find "$test_library_dir" -maxdepth 1 \( -type f -o -type l \) \ + \( -name 'libcalculator.so*' -o -name 'libhap_example.so*' -o -name 'libmultithreading.so*' \) \ + -print -quit 2>/dev/null | grep -q .; then + FASTRPC_RESOLVED_LIB_TEST_PATH="$( + fastrpc_append_colon_dir "$FASTRPC_RESOLVED_LIB_TEST_PATH" "$test_library_dir" + )" + fi + done + if [ -n "${FASTRPC_HEALTHCHECK_DSP_LIB_DIR:-}" ]; then + FASTRPC_SKEL_BASES_CHECKED="$( + fastrpc_append_word_unique \ + "$FASTRPC_SKEL_BASES_CHECKED" \ + "$FASTRPC_HEALTHCHECK_DSP_LIB_DIR" + )" fi + + FASTRPC_RESOLVED_SKEL_PATH="$(fastrpc_find_skeleton_dirs "$FASTRPC_SKEL_BASES_CHECKED")" +} + +# fastrpc_missing_test_libraries +# Identify required host test libraries absent from the discovered search directories. +# Inputs: FASTRPC_LIB_TEST_DIRS_CHECKED. Output: space-separated names on stdout. +# Returns: 0. Side effects: read-only filesystem traversal. +fastrpc_missing_test_libraries() { + missing_libraries="" + + for test_library in libcalculator.so libhap_example.so libmultithreading.so; do + found_library=0 + + for test_dir in $FASTRPC_LIB_TEST_DIRS_CHECKED; do + if find "$test_dir" -maxdepth 1 \( -type f -o -type l \) \ + -name "${test_library}*" -print -quit 2>/dev/null | + grep -q .; then + found_library=1 + break + fi + done + + if [ "$found_library" -eq 0 ]; then + missing_libraries="$(fastrpc_append_word_unique "$missing_libraries" "$test_library")" + fi + done + + printf '%s\n' "$missing_libraries" } +# fastrpc_missing_skeleton_libraries +# Identify required DSP skeleton libraries absent from the discovered search trees. +# Inputs: FASTRPC_SKEL_BASES_CHECKED. Output: space-separated names on stdout. +# Returns: 0. Side effects: read-only filesystem traversal. +fastrpc_missing_skeleton_libraries() { + missing_skeletons="" + + for skeleton_library in \ + libcalculator_skel.so \ + libhap_example_skel.so \ + libmultithreading_skel.so; do + found_skeleton=0 + + for skeleton_base in $FASTRPC_SKEL_BASES_CHECKED; do + if find "$skeleton_base" \( -type f -o -type l \) \ + -name "${skeleton_library}*" -print -quit 2>/dev/null | + grep -q .; then + found_skeleton=1 + break + fi + done + + if [ "$found_skeleton" -eq 0 ]; then + missing_skeletons="$( + fastrpc_append_word_unique "$missing_skeletons" "$skeleton_library" + )" + fi + done + + printf '%s\n' "$missing_skeletons" +} + +# fastrpc_validate_runtime_artifacts +# Validate the FastRPC binary's host dependencies and all required test artifacts. +# Inputs: RUN_BIN and discovered FASTRPC_* globals. Output: no stdout contract. +# Returns: 0 when usable, 1 otherwise, and sets FASTRPC_ARTIFACT_ERROR. +# Side effects: runs read-only find and optional ldd probes. +fastrpc_validate_runtime_artifacts() { + FASTRPC_ARTIFACT_ERROR="" + missing_test_libraries="$(fastrpc_missing_test_libraries)" + missing_skeleton_libraries="$(fastrpc_missing_skeleton_libraries)" + + if [ -z "$FASTRPC_RESOLVED_LIB_SYS_DIR" ]; then + FASTRPC_ARTIFACT_ERROR="no FastRPC system library was found" + elif [ -n "$missing_test_libraries" ]; then + FASTRPC_ARTIFACT_ERROR="missing test libraries: $missing_test_libraries" + elif [ -n "$missing_skeleton_libraries" ]; then + FASTRPC_ARTIFACT_ERROR="missing DSP skeleton libraries: $missing_skeleton_libraries" + elif [ -z "$FASTRPC_RESOLVED_SKEL_PATH" ]; then + FASTRPC_ARTIFACT_ERROR="no directory containing required DSP skeleton libraries was found" + elif command -v ldd >/dev/null 2>&1 && + ldd "$RUN_BIN" 2>/dev/null | grep -q 'not found'; then + FASTRPC_ARTIFACT_ERROR="fastrpc_test has unresolved host library dependencies" + fi + + if [ -z "$FASTRPC_ARTIFACT_ERROR" ] && command -v ldd >/dev/null 2>&1; then + for test_library in libcalculator.so libhap_example.so libmultithreading.so; do + test_library_path="$( + fastrpc_find_file_in_dirs \ + "$FASTRPC_LIB_TEST_DIRS_CHECKED" \ + "${test_library}*" || true + )" + + if [ -n "$test_library_path" ] && + ldd "$test_library_path" 2>/dev/null | grep -q 'not found'; then + FASTRPC_ARTIFACT_ERROR="$test_library has unresolved host library dependencies" + break + fi + done + fi + + [ -z "$FASTRPC_ARTIFACT_ERROR" ] +} + +# fastrpc_export_runtime_env +# Build and export host and DSP library search paths from discovered directories. +# Inputs: FASTRPC_RESOLVED_* globals and existing library-path environment values. +# Outputs: exported LD_LIBRARY_PATH and DSP-specific *_LIBRARY_PATH variables. +# Returns: 0. Side effects: mutates the current shell environment. fastrpc_export_runtime_env() { new_ld_library_path="" - new_ld_library_path="$(fastrpc_append_colon_dir "$new_ld_library_path" "$FASTRPC_RESOLVED_LIB_SYS_DIR")" - new_ld_library_path="$(fastrpc_append_colon_dir "$new_ld_library_path" "$FASTRPC_RESOLVED_LIB_TEST_DIR")" + for resolved_dir in $(printf '%s' "$FASTRPC_RESOLVED_LIB_SYS_PATH" | tr ':' ' '); do + new_ld_library_path="$(fastrpc_append_colon_dir "$new_ld_library_path" "$resolved_dir")" + done + + for resolved_dir in $(printf '%s' "$FASTRPC_RESOLVED_LIB_TEST_PATH" | tr ':' ' '); do + new_ld_library_path="$(fastrpc_append_colon_dir "$new_ld_library_path" "$resolved_dir")" + done if [ -n "${LD_LIBRARY_PATH:-}" ]; then if [ -n "$new_ld_library_path" ]; then @@ -188,17 +424,21 @@ fastrpc_export_runtime_env() { fi } +# fastrpc_log_runtime_layout +# Log discovered FastRPC paths and the resolved location of every required artifact. +# Inputs: discovered FASTRPC_* globals. Output: no machine-readable stdout contract. +# Returns: 0. Side effects: emits informational and warning log records. fastrpc_log_runtime_layout() { log_info "FastRPC multiarch triplet: ${FASTRPC_MULTIARCH_TRIPLET:-}" - if [ -n "$FASTRPC_RESOLVED_LIB_SYS_DIR" ]; then - log_info "FastRPC system library dir: $FASTRPC_RESOLVED_LIB_SYS_DIR" + if [ -n "$FASTRPC_RESOLVED_LIB_SYS_PATH" ]; then + log_info "FastRPC system library path: $FASTRPC_RESOLVED_LIB_SYS_PATH" else log_warn "No FastRPC system library dir found. Checked: $FASTRPC_LIB_SYS_DIRS_CHECKED" fi - if [ -n "$FASTRPC_RESOLVED_LIB_TEST_DIR" ]; then - log_info "FastRPC test library dir: $FASTRPC_RESOLVED_LIB_TEST_DIR" + if [ -n "$FASTRPC_RESOLVED_LIB_TEST_PATH" ]; then + log_info "FastRPC test library path: $FASTRPC_RESOLVED_LIB_TEST_PATH" else log_warn "No FastRPC test library dir found. Checked: $FASTRPC_LIB_TEST_DIRS_CHECKED" fi @@ -208,8 +448,42 @@ fastrpc_log_runtime_layout() { else log_warn "No DSP skeleton dirs found. Checked bases: $FASTRPC_SKEL_BASES_CHECKED" fi + + for runtime_library in libadsprpc.so libcdsprpc.so libsdsprpc.so; do + runtime_library_path="$( + fastrpc_find_file_in_dirs \ + "$FASTRPC_LIB_SYS_DIRS_CHECKED" \ + "${runtime_library}*" || true + )" + log_info "[FASTRPC-ARTIFACT] type=runtime-library name=$runtime_library path=${runtime_library_path:-missing}" + done + + for test_library in libcalculator.so libhap_example.so libmultithreading.so; do + test_library_path="$( + fastrpc_find_file_in_dirs \ + "$FASTRPC_LIB_TEST_DIRS_CHECKED" \ + "${test_library}*" || true + )" + log_info "[FASTRPC-ARTIFACT] type=test-library name=$test_library path=${test_library_path:-missing}" + done + + for skeleton_library in \ + libcalculator_skel.so \ + libhap_example_skel.so \ + libmultithreading_skel.so; do + skeleton_library_path="$( + fastrpc_find_file_in_trees \ + "$FASTRPC_SKEL_BASES_CHECKED" \ + "${skeleton_library}*" || true + )" + log_info "[FASTRPC-ARTIFACT] type=dsp-skeleton name=$skeleton_library path=${skeleton_library_path:-missing}" + done } +# fastrpc_setup_runtime_layout +# Discover, report, and export the complete FastRPC runtime library layout. +# Inputs: optional FASTRPC_* path overrides. Output: discovered/exported globals. +# Returns: 0. Side effects: logs discovery and mutates library-path environment values. fastrpc_setup_runtime_layout() { fastrpc_discover_runtime_layout fastrpc_log_runtime_layout @@ -223,6 +497,10 @@ fastrpc_setup_runtime_layout() { # -------------------- FastRPC test orchestration helpers -------------------- +# log_debug MESSAGE... +# Emit a diagnostic message only when VERBOSE is set to 1. +# Inputs: message arguments and VERBOSE. Output: no stdout contract. +# Returns: 0. Side effects: logs to stderr when enabled. # shellcheck disable=SC2317 log_debug() { if [ "${VERBOSE:-0}" -eq 1 ]; then @@ -230,6 +508,10 @@ log_debug() { fi } +# cmd_to_string ARG... +# Render command arguments as a readable shell-like string for diagnostic logging. +# Inputs: arbitrary command arguments. Output: one escaped string on stdout. +# Returns: 0. Side effects: none. The output is descriptive and must not be evaluated. cmd_to_string() { out="" for a in "$@"; do @@ -246,6 +528,10 @@ cmd_to_string() { printf "%s" "$out" } +# extract_test_summary_counts LOG_FILE +# Parse the final FastRPC test totals from a retained command log. +# Input: readable log path. Output: total:passed:failed:skipped on stdout. +# Returns: 0 and substitutes zero for missing or malformed fields. Side effects: none. extract_test_summary_counts() { log_file="$1" @@ -262,18 +548,12 @@ extract_test_summary_counts() { printf '%s:%s:%s:%s\n' "$total" "$passed" "$failed" "$skipped" } -# Returns true if the only failing subtest is libhap_example.so. -# Used to treat HAP_mem DMA failures as known-skip on affected SoCs. -only_hap_example_failed() { - log_file="$1" - - [ -r "$log_file" ] || return 1 - grep -F -q "[FAIL]" "$log_file" || return 1 - ! grep -F "[FAIL]" "$log_file" | grep -q -v "libhap_example.so" -} - +# log_dsp_remoteproc_status +# Log runtime remoteproc state for every FastRPC firmware alias in the domain table. +# Inputs: runtime DT and remoteproc sysfs through shared helpers. Output: no stdout contract. +# Returns: 0. Side effects: emits informational logs only. log_dsp_remoteproc_status() { - fw_list="adsp mdsp sdsp cdsp cdsp0 cdsp1 gdsp0 gdsp1 gpdsp0 gpdsp1" + fw_list="$(fastrpc_domain_table | awk -F '|' '{print $5}')" any=0 for fw in $fw_list; do @@ -297,32 +577,79 @@ __RPROC__ [ "$any" -eq 0 ] && log_info "rproc: no *dsp remoteproc entries detected via DT" } +# fastrpc_domain_table +# Emit the protocol-defined FastRPC domain metadata used by discovery and policy. +# Inputs: none. Output: pipe-separated domain rows on stdout. +# Returns: 0. Side effects: none. +fastrpc_domain_table() { + cat <<'EOF' +0|ADSP|adsp|adsp|adsp|0 +1|MDSP|mdsp|mdsp|mdsp|0 +2|SDSP|sdsp|sdsp|sdsp|0 +3|CDSP|cdsp|cdsp|cdsp cdsp0|0 1 +4|CDSP1|cdsp1|cdsp1|cdsp1|0 1 +5|GDSP0|gdsp0|gdsp0|gdsp0 gpdsp0|0 1 +6|GDSP1|gdsp1|gdsp1|gdsp1 gpdsp1|0 1 +EOF +} + +# fastrpc_domain_field DOMAIN_ID FIELD_NUMBER +# Read one field from the FastRPC domain table for a numeric domain identifier. +# Inputs: domain ID and one-based field number. Output: matching field on stdout. +# Returns: awk status, normally 0 even when no row matches. Side effects: none. +fastrpc_domain_field() { + requested_domain="$1" + requested_field="$2" + + fastrpc_domain_table | awk -F '|' -v domain="$requested_domain" -v field="$requested_field" ' + $1 == domain { + print $field + exit + } + ' +} + +# name_to_domain DOMAIN_NAME +# Map a case-insensitive public domain name or supported alias to its numeric ID. +# Input: one domain name. Output: numeric domain ID on stdout when recognized. +# Returns: awk status, normally 0 even when unmapped. Side effects: none. name_to_domain() { - case "$(printf "%s" "$1" | tr '[:upper:]' '[:lower:]')" in - adsp) echo 0 ;; - mdsp) echo 1 ;; - sdsp) echo 2 ;; - cdsp) echo 3 ;; - cdsp1) echo 4 ;; - gpdsp0|gdsp0) echo 5 ;; - gpdsp1|gdsp1) echo 6 ;; - *) echo "" ;; - esac + requested_name="$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')" + + fastrpc_domain_table | awk -F '|' -v requested="$requested_name" ' + { + alias_count = split($5, aliases, " ") + for (alias_number = 1; alias_number <= alias_count; alias_number++) { + if (requested == aliases[alias_number]) { + print $1 + exit + } + } + } + ' } +# domain_to_name DOMAIN_ID +# Map a numeric FastRPC domain identifier to its public display name. +# Input: numeric domain ID. Output: domain name or UNKNOWN on stdout. +# Returns: 0. Side effects: none. domain_to_name() { - case "$1" in - 0) echo "ADSP" ;; - 1) echo "MDSP" ;; - 2) echo "SDSP" ;; - 3) echo "CDSP" ;; - 4) echo "CDSP1" ;; - 5) echo "GPDSP0" ;; - 6) echo "GPDSP1" ;; - *) echo "UNKNOWN" ;; - esac + domain_name="$(fastrpc_domain_field "$1" 2)" + printf '%s\n' "${domain_name:-UNKNOWN}" } +# domain_to_endpoint_label DOMAIN_ID +# Return the device-node label associated with a FastRPC domain. +# Input: numeric domain ID. Output: endpoint label or an empty line on stdout. +# Returns: the domain-table lookup status. Side effects: none. +domain_to_endpoint_label() { + fastrpc_domain_field "$1" 3 +} + +# append_unique CURRENT NEW +# Append NEW to a space-separated list when it is not already present. +# Inputs: current list and new word. Output: resulting list on stdout. +# Returns: 0. Side effects: none. append_unique() { current="$1" new="$2" @@ -337,21 +664,246 @@ append_unique() { [ -n "$current" ] && printf "%s %s" "$current" "$new" || printf "%s" "$new" } -canonicalize_domain_name() { - norm="$(printf "%s" "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')" +# fastrpc_capture_healthcheck LOG_FILE TIMEOUT_SECONDS +# Run the image-provided FastRPC healthcheck under the shared bounded runner. +# Inputs: retained log path, positive timeout, and optional FASTRPC_HEALTHCHECK_BIN. +# Output: no stdout contract. Returns: 0 on success, 1 on execution failure, 2 if absent. +# Side effects: truncates LOG_FILE and emits healthcheck diagnostic logs. +fastrpc_capture_healthcheck() { + healthcheck_log="$1" + healthcheck_timeout="$2" + healthcheck_bin="${FASTRPC_HEALTHCHECK_BIN:-}" + + if [ -z "$healthcheck_bin" ]; then + healthcheck_bin="$(command -v fastrpc-healthcheck 2>/dev/null || true)" + fi + + if [ -z "$healthcheck_bin" ] && [ -x "${BIN_DIR:-/usr/bin}/fastrpc-healthcheck" ]; then + healthcheck_bin="${BIN_DIR:-/usr/bin}/fastrpc-healthcheck" + fi + + [ -n "$healthcheck_bin" ] || return 2 + + : >"$healthcheck_log" + log_info "[FASTRPC-HEALTHCHECK] command=$healthcheck_bin timeout=${healthcheck_timeout}s artifact=$healthcheck_log" + + diag_run_with_timeout "$healthcheck_timeout" "$healthcheck_bin" >"$healthcheck_log" 2>&1 + healthcheck_rc=$? + + if [ "$healthcheck_rc" -eq 0 ]; then + return 0 + fi + + log_warn "[FASTRPC-HEALTHCHECK] command failed rc=$healthcheck_rc artifact=$healthcheck_log" + return 1 +} + +# fastrpc_parse_healthcheck LOG_FILE TSV_FILE +# Parse the healthcheck DSP capability table and advertised DSP library path. +# Inputs: raw healthcheck log and destination TSV path. Output: no stdout contract. +# Returns: 0 when at least one DSP row is parsed, 1 otherwise. +# Side effects: replaces TSV_FILE and sets FASTRPC_HEALTHCHECK_DSP_LIB_DIR. +fastrpc_parse_healthcheck() { + healthcheck_log="$1" + healthcheck_tsv="$2" + + FASTRPC_HEALTHCHECK_DSP_LIB_DIR="$( + sed -n 's/^[[:space:]]*DSP dynamic libs path[[:space:]]*:[[:space:]]*//p' \ + "$healthcheck_log" | tail -n 1 + )" + + awk ' + BEGIN { + OFS = "\t" + in_table = 0 + } + $1 == "DSP" && $2 == "State" && $3 == "SignedPD" { + in_table = 1 + next + } + in_table && $1 ~ /^-+$/ { + next + } + in_table && NF >= 5 { + name = toupper($1) + if (name ~ /^(ADSP|MDSP|SDSP|CDSP|CDSP1|GDSP0|GDSP1|GPDSP0|GPDSP1)$/) { + support = tolower($5) + for (column = 6; column <= NF; column++) { + support = support " " tolower($column) + } + print name, tolower($2), tolower($3), tolower($4), support + } + } + ' "$healthcheck_log" >"$healthcheck_tsv" + + [ -s "$healthcheck_tsv" ] +} - case "$norm" in - cdsp0) echo "cdsp" ;; - gdsp0) echo "gpdsp0" ;; - gdsp1) echo "gpdsp1" ;; - *) printf "%s" "$norm" ;; +# fastrpc_healthcheck_record DOMAIN_ID +# Select the parsed healthcheck row for a domain, accepting public GDSP aliases. +# Input: numeric domain ID and FASTRPC_HEALTHCHECK_TSV. Output: one TSV row on stdout. +# Returns: awk status, normally 0 even when no row matches. Side effects: none. +fastrpc_healthcheck_record() { + domain_id="$1" + healthcheck_name="$(fastrpc_domain_field "$domain_id" 2)" + + case "$healthcheck_name" in + GDSP0) healthcheck_pattern='GDSP0|GPDSP0' ;; + GDSP1) healthcheck_pattern='GDSP1|GPDSP1' ;; + *) healthcheck_pattern="$healthcheck_name" ;; esac + + awk -F '\t' -v names="$healthcheck_pattern" ' + BEGIN { + count = split(names, accepted, "|") + } + { + for (item = 1; item <= count; item++) { + if ($1 == accepted[item]) { + print + exit + } + } + } + ' "${FASTRPC_HEALTHCHECK_TSV:-/dev/null}" +} + +# fastrpc_log_healthcheck_records +# Log every parsed FastRPC domain capability record for live CI evidence. +# Input: FASTRPC_HEALTHCHECK_TSV. Output: no machine-readable stdout contract. +# Returns: 0. Side effects: emits informational logs. +fastrpc_log_healthcheck_records() { + while IFS="$(printf '\t')" read -r hc_name hc_state hc_signed hc_unsigned hc_support; do + [ -n "$hc_name" ] || continue + log_info "[FASTRPC-CAPABILITY] domain=$hc_name state=$hc_state signed_pd=$hc_signed unsigned_pd=$hc_unsigned support=$hc_support source=fastrpc-healthcheck" + done <"$FASTRPC_HEALTHCHECK_TSV" } +# fastrpc_healthcheck_supported_domains +# Select online, FastRPC-supported domain IDs from the parsed healthcheck table. +# Input: FASTRPC_HEALTHCHECK_TSV. Output: one numeric domain ID per stdout line. +# Returns: 0. Side effects: logs exclusion and mapping diagnostics to stderr. +fastrpc_healthcheck_supported_domains() { + while IFS="$(printf '\t')" read -r \ + hc_name hc_state hc_signed hc_unsigned hc_support; do + if [ "$hc_state" != "online" ]; then + log_info "[FASTRPC-DISCOVERY] domain=$hc_name eligible=no reason=healthcheck-state-$hc_state" >&2 + continue + fi + + if [ "$hc_support" != "yes" ]; then + log_info "[FASTRPC-DISCOVERY] domain=$hc_name eligible=no reason=healthcheck-support-$hc_support" >&2 + continue + fi + + hc_domain="$(name_to_domain "$hc_name")" + if [ -n "$hc_domain" ]; then + printf '%s\n' "$hc_domain" + else + log_warn "[FASTRPC-DISCOVERY] domain=$hc_name eligible=no reason=unmapped-healthcheck-domain" >&2 + fi + done <"$FASTRPC_HEALTHCHECK_TSV" +} + +# fastrpc_healthcheck_domain_ready DOMAIN_ID +# Enforce that an explicitly selected domain is listed, online, and supported. +# Input: numeric domain ID and FASTRPC_HEALTHCHECK_TSV. Output: no stdout contract. +# Returns: 0 when ready, 1 when unhealthy, 2 when unlisted. +# Side effects: emits failure diagnostics. +fastrpc_healthcheck_domain_ready() { + healthcheck_record="$(fastrpc_healthcheck_record "$1")" + if [ -z "$healthcheck_record" ]; then + log_fail "[FASTRPC-CAPABILITY] domain=$(domain_to_name "$1") expected=healthcheck-record observed=not-listed artifact=$FASTRPC_HEALTHCHECK_TSV" + return 2 + fi + + IFS="$(printf '\t')" read -r hc_name hc_state hc_signed hc_unsigned hc_support </dev/null || true + )" + [ -n "$remoteproc_entries" ] || continue + + while IFS='|' read -r remoteproc_path remoteproc_state remoteproc_firmware remoteproc_name; do + [ -n "$remoteproc_path" ] || continue + printf '%s|%s|%s|%s\n' \ + "$remoteproc_state" \ + "$remoteproc_path" \ + "$remoteproc_firmware" \ + "$remoteproc_name" + return 0 + done <&2 - for fw in adsp mdsp sdsp cdsp cdsp0 cdsp1 gpdsp0 gpdsp1 gdsp0 gdsp1; do + if [ -s "${FASTRPC_HEALTHCHECK_TSV:-}" ]; then + fastrpc_healthcheck_supported_domains + return + fi + + for fw in $(fastrpc_domain_table | awk -F '|' '{print $5}'); do entries="$(get_remoteproc_by_firmware "$fw" "" all 2>/dev/null || true)" if [ -n "$entries" ]; then @@ -366,25 +918,37 @@ discover_supported_domains() { [ -n "$nameguess" ] || continue - canon="$(canonicalize_domain_name "$nameguess")" - d="$(name_to_domain "$canon")" + d="$(name_to_domain "$nameguess")" if [ -n "$d" ]; then - printf '%s\n' "$d" - log_debug "discover: fw=$fw rname=$rname rfirm=$rfirm canon=$canon domain=$d state=$rstate" + if [ "$rstate" != "running" ]; then + log_info "[FASTRPC-DISCOVERY] domain=$(domain_to_name "$d") eligible=no reason=remoteproc-state-$rstate" >&2 + else + if fastrpc_domain_endpoint_available "$d"; then + printf '%s\n' "$d" + log_debug "discover: fw=$fw rname=$rname rfirm=$rfirm domain=$d state=$rstate" + else + label="$(domain_to_endpoint_label "$d")" + log_info "[FASTRPC-DISCOVERY] domain=$(domain_to_name "$d") eligible=no reason=endpoint-absent expected=/dev/fastrpc-${label}[-secure]" >&2 + fi + fi else - log_debug "discover: fw=$fw rname=$rname rfirm=$rfirm canon=$canon domain=" + log_debug "discover: fw=$fw rname=$rname rfirm=$rfirm domain=" fi done <&2 + else + printf '%s\n' "$d" + log_debug "discover: fw=$fw dt-only domain=$d" + fi fi else log_debug "discover: fw=$fw not present" @@ -392,6 +956,11 @@ EOF done } +# resolve_domains_to_test +# Apply CLI, environment, and dynamic-discovery precedence to the domain selection. +# Inputs: CLI_DOMAIN*, DOMAIN_MODE, FASTRPC_DOMAIN*, and runtime discovery. +# Output: unique space-separated valid domain IDs on stdout. Returns: 0. +# Side effects: logs invalid entries and verbose diagnostics. resolve_domains_to_test() { resolved="" @@ -413,38 +982,59 @@ resolve_domains_to_test() { valid="" for d in $resolved; do - case "$d" in - 0|1|2|3|4|5|6) - case " $valid " in - *" $d "*) - : - ;; - *) - if [ -n "$valid" ]; then - valid="${valid} ${d}" - else - valid="$d" - fi - ;; - esac - ;; - *) - log_warn "Ignoring invalid domain '$d'" - ;; - esac + if [ -n "$(fastrpc_domain_field "$d" 2)" ]; then + case " $valid " in + *" $d "*) + : + ;; + *) + if [ -n "$valid" ]; then + valid="${valid} ${d}" + else + valid="$d" + fi + ;; + esac + else + log_warn "Ignoring invalid domain '$d'" + fi done printf '%s' "$valid" } +# domain_supported_pds DOMAIN_ID +# Return signed and unsigned protection-domain modes supported by one DSP domain. +# Input: numeric domain ID and optional healthcheck table. Output: space-separated 0/1 values. +# Returns: 0. Side effects: none. domain_supported_pds() { - case "$1" in - 0|1|2) printf "%s" "0" ;; - 3|4|5|6) printf "%s" "0 1" ;; - *) printf "%s" "" ;; - esac + domain_id="$1" + + if [ -s "${FASTRPC_HEALTHCHECK_TSV:-}" ]; then + healthcheck_record="$(fastrpc_healthcheck_record "$domain_id")" + + if [ -n "$healthcheck_record" ]; then + IFS="$(printf '\t')" read -r \ + hc_name hc_state hc_signed hc_unsigned hc_support < Date: Sun, 20 Sep 2026 21:30:30 +0530 Subject: [PATCH 2/2] fastrpc-test: harden capability failure paths Fail when an installed healthcheck cannot provide trustworthy capability data instead of silently using weaker fallback evidence. Validate the runtime library required by every selected domain, prefer running remoteproc matches, and restore the runner executable mode. Signed-off-by: Srikanth Muppandam --- .../CDSP/fastrpc_test/fastrpc_test_README.md | 10 ++- .../Multimedia/CDSP/fastrpc_test/run.sh | 16 +++- Runner/utils/lib_fastrpc.sh | 84 ++++++++++++++----- 3 files changed, 84 insertions(+), 26 deletions(-) mode change 100644 => 100755 Runner/suites/Multimedia/CDSP/fastrpc_test/run.sh diff --git a/Runner/suites/Multimedia/CDSP/fastrpc_test/fastrpc_test_README.md b/Runner/suites/Multimedia/CDSP/fastrpc_test/fastrpc_test_README.md index 2410be81a..9f1518ba7 100644 --- a/Runner/suites/Multimedia/CDSP/fastrpc_test/fastrpc_test_README.md +++ b/Runner/suites/Multimedia/CDSP/fastrpc_test/fastrpc_test_README.md @@ -32,7 +32,8 @@ Have these on the target (or specify paths with the flags below): - `fastrpc_test` binary (from [github.com/quic/fastrpc](https://github.com/quic/fastrpc)) - Optional `fastrpc-healthcheck`. When installed, this is the primary distro-independent - capability source. + capability source. An installed healthcheck that fails, times out, or produces an + unrecognized report fails the suite. Runtime fallback is used only when the tool is absent. - FastRPC system libraries and DSP skeletons auto-discovered from standard locations: - Yocto: `/usr/local/lib`, `/usr/local/lib/fastrpc_test`, `/usr/local/share/fastrpc_test` - Debian: `/usr/lib/`, `/usr/lib//fastrpc_test`, `/usr/share/fastrpc_test` @@ -163,7 +164,7 @@ ALLOW_BIN_FASTRPC=1 ./run.sh --bin-dir /bin domains continue. - **Offline explicitly requested domain**: Fails with the healthcheck or remoteproc state. - **Missing runtime artifacts**: Skips before executing `fastrpc_test` and prints the missing - system library, test library, or skeleton evidence. + per-domain system library, test library, or skeleton evidence. - **Binary resolved to /bin/fastrpc_test**: Blocked by default. Set `ALLOW_BIN_FASTRPC=1` or use `--bin-dir` to a non-`/bin` path. - **Session create errors with -U 1**: If unsigned PD returns `0x80000416`, confirm your image @@ -176,8 +177,9 @@ ALLOW_BIN_FASTRPC=1 ./run.sh --bin-dir /bin ## Notes -- Domain and PD support are derived from `fastrpc-healthcheck` when available. The fallback uses - runtime remoteproc and endpoint evidence, with a conservative protocol mapping for PD support. +- Domain and PD support are derived from `fastrpc-healthcheck` when available. The fallback is + selected only when the tool is absent and uses runtime remoteproc and endpoint evidence, with + a conservative protocol mapping for PD support. - This suite runs the public `fastrpc_test` character-device path and therefore requires either `/dev/fastrpc-` or `/dev/fastrpc--secure` for every selected domain. - DSP skeleton directories are discovered dynamically by locating `.so` artifacts instead of diff --git a/Runner/suites/Multimedia/CDSP/fastrpc_test/run.sh b/Runner/suites/Multimedia/CDSP/fastrpc_test/run.sh old mode 100644 new mode 100755 index 31a4d425f..901646df9 --- a/Runner/suites/Multimedia/CDSP/fastrpc_test/run.sh +++ b/Runner/suites/Multimedia/CDSP/fastrpc_test/run.sh @@ -328,17 +328,27 @@ case "$healthcheck_status" in fastrpc_log_healthcheck_records else rm -f "$FASTRPC_HEALTHCHECK_TSV" - log_warn "[FASTRPC-POLICY] capability_source=runtime-fallback domain_source=remoteproc-endpoint pd_source=protocol-fallback-map reason=healthcheck-output-unrecognized artifact=$FASTRPC_HEALTHCHECK_LOG" + log_fail "$TESTNAME FAIL - installed fastrpc-healthcheck produced unrecognized output, artifact=$FASTRPC_HEALTHCHECK_LOG" + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 fi ;; 1) rm -f "$FASTRPC_HEALTHCHECK_TSV" - log_warn "[FASTRPC-POLICY] capability_source=runtime-fallback domain_source=remoteproc-endpoint pd_source=protocol-fallback-map reason=healthcheck-failed artifact=$FASTRPC_HEALTHCHECK_LOG" + log_fail "$TESTNAME FAIL - installed fastrpc-healthcheck execution failed, artifact=$FASTRPC_HEALTHCHECK_LOG" + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 ;; 2) rm -f "$FASTRPC_HEALTHCHECK_TSV" log_info "[FASTRPC-POLICY] capability_source=runtime-fallback domain_source=remoteproc-endpoint pd_source=protocol-fallback-map reason=fastrpc-healthcheck-not-installed" ;; + *) + rm -f "$FASTRPC_HEALTHCHECK_TSV" + log_fail "$TESTNAME FAIL - unexpected fastrpc-healthcheck status=$healthcheck_status, artifact=$FASTRPC_HEALTHCHECK_LOG" + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 + ;; esac # -------------------- Runtime layout discovery -------------------- @@ -376,7 +386,7 @@ if [ -z "$DOMAINS_TO_TEST" ]; then exit 0 fi -if ! fastrpc_validate_runtime_artifacts; then +if ! fastrpc_validate_runtime_artifacts "$DOMAINS_TO_TEST"; then log_skip "$TESTNAME SKIP - FastRPC test artifacts are incomplete: $FASTRPC_ARTIFACT_ERROR" echo "$TESTNAME : SKIP" >"$RESULT_FILE" exit 0 diff --git a/Runner/utils/lib_fastrpc.sh b/Runner/utils/lib_fastrpc.sh index 092686136..a2e1e1182 100755 --- a/Runner/utils/lib_fastrpc.sh +++ b/Runner/utils/lib_fastrpc.sh @@ -343,17 +343,49 @@ fastrpc_missing_skeleton_libraries() { printf '%s\n' "$missing_skeletons" } -# fastrpc_validate_runtime_artifacts -# Validate the FastRPC binary's host dependencies and all required test artifacts. -# Inputs: RUN_BIN and discovered FASTRPC_* globals. Output: no stdout contract. +# fastrpc_missing_domain_system_libraries DOMAIN_IDS +# Identify runtime libraries absent for the selected FastRPC domains. +# Input: space-separated domain IDs. Output: space-separated DOMAIN:LIBRARY entries. +# Returns: 0. Side effects: read-only filesystem traversal. +fastrpc_missing_domain_system_libraries() { + selected_domains="$1" + missing_libraries="" + + for selected_domain in $selected_domains; do + required_library="$(fastrpc_domain_field "$selected_domain" 7)" + [ -n "$required_library" ] || continue + + if ! fastrpc_find_file_in_dirs \ + "$FASTRPC_LIB_SYS_DIRS_CHECKED" \ + "${required_library}*" >/dev/null; then + missing_entry="$(domain_to_name "$selected_domain"):${required_library}" + missing_libraries="$( + fastrpc_append_word_unique "$missing_libraries" "$missing_entry" + )" + fi + done + + printf '%s\n' "$missing_libraries" +} + +# fastrpc_validate_runtime_artifacts DOMAIN_IDS +# Validate the FastRPC binary's host dependencies and selected-domain artifacts. +# Inputs: space-separated domain IDs, RUN_BIN, and discovered FASTRPC_* globals. +# Output: no stdout contract. # Returns: 0 when usable, 1 otherwise, and sets FASTRPC_ARTIFACT_ERROR. # Side effects: runs read-only find and optional ldd probes. fastrpc_validate_runtime_artifacts() { + selected_domains="$1" FASTRPC_ARTIFACT_ERROR="" + missing_domain_libraries="$( + fastrpc_missing_domain_system_libraries "$selected_domains" + )" missing_test_libraries="$(fastrpc_missing_test_libraries)" missing_skeleton_libraries="$(fastrpc_missing_skeleton_libraries)" - if [ -z "$FASTRPC_RESOLVED_LIB_SYS_DIR" ]; then + if [ -n "$missing_domain_libraries" ]; then + FASTRPC_ARTIFACT_ERROR="missing selected-domain system libraries: $missing_domain_libraries" + elif [ -z "$FASTRPC_RESOLVED_LIB_SYS_DIR" ]; then FASTRPC_ARTIFACT_ERROR="no FastRPC system library was found" elif [ -n "$missing_test_libraries" ]; then FASTRPC_ARTIFACT_ERROR="missing test libraries: $missing_test_libraries" @@ -449,7 +481,10 @@ fastrpc_log_runtime_layout() { log_warn "No DSP skeleton dirs found. Checked bases: $FASTRPC_SKEL_BASES_CHECKED" fi - for runtime_library in libadsprpc.so libcdsprpc.so libsdsprpc.so; do + runtime_libraries="$( + fastrpc_domain_table | awk -F '|' '!seen[$7]++ {print $7}' + )" + for runtime_library in $runtime_libraries; do runtime_library_path="$( fastrpc_find_file_in_dirs \ "$FASTRPC_LIB_SYS_DIRS_CHECKED" \ @@ -579,17 +614,18 @@ __RPROC__ # fastrpc_domain_table # Emit the protocol-defined FastRPC domain metadata used by discovery and policy. +# Fields: ID, name, endpoint, URI suffix, firmware aliases, PD modes, runtime library. # Inputs: none. Output: pipe-separated domain rows on stdout. # Returns: 0. Side effects: none. fastrpc_domain_table() { cat <<'EOF' -0|ADSP|adsp|adsp|adsp|0 -1|MDSP|mdsp|mdsp|mdsp|0 -2|SDSP|sdsp|sdsp|sdsp|0 -3|CDSP|cdsp|cdsp|cdsp cdsp0|0 1 -4|CDSP1|cdsp1|cdsp1|cdsp1|0 1 -5|GDSP0|gdsp0|gdsp0|gdsp0 gpdsp0|0 1 -6|GDSP1|gdsp1|gdsp1|gdsp1 gpdsp1|0 1 +0|ADSP|adsp|adsp|adsp|0|libadsprpc.so +1|MDSP|mdsp|mdsp|mdsp|0|libmdsprpc.so +2|SDSP|sdsp|sdsp|sdsp|0|libsdsprpc.so +3|CDSP|cdsp|cdsp|cdsp cdsp0|0 1|libcdsprpc.so +4|CDSP1|cdsp1|cdsp1|cdsp1|0 1|libcdsprpc.so +5|GDSP0|gdsp0|gdsp0|gdsp0 gpdsp0|0 1|libcdsprpc.so +6|GDSP1|gdsp1|gdsp1|gdsp1 gpdsp1|0 1|libcdsprpc.so EOF } @@ -830,12 +866,13 @@ EOF } # fastrpc_remoteproc_domain_state DOMAIN_ID -# Resolve the first runtime remoteproc record matching a domain's firmware aliases. +# Resolve a running remoteproc record, retaining the first match for diagnostics. # Input: numeric domain ID. Output: state|path|firmware|name on stdout. # Returns: 0 when found, 2 otherwise. Side effects: read-only shared-helper probes. fastrpc_remoteproc_domain_state() { domain_id="$1" domain_aliases="$(fastrpc_domain_field "$domain_id" 5)" + fallback_record="" for domain_alias in $domain_aliases; do remoteproc_entries="$( @@ -845,17 +882,26 @@ fastrpc_remoteproc_domain_state() { while IFS='|' read -r remoteproc_path remoteproc_state remoteproc_firmware remoteproc_name; do [ -n "$remoteproc_path" ] || continue - printf '%s|%s|%s|%s\n' \ - "$remoteproc_state" \ - "$remoteproc_path" \ - "$remoteproc_firmware" \ - "$remoteproc_name" - return 0 + remoteproc_record="${remoteproc_state}|${remoteproc_path}|${remoteproc_firmware}|${remoteproc_name}" + + if [ "$remoteproc_state" = "running" ]; then + printf '%s\n' "$remoteproc_record" + return 0 + fi + + if [ -z "$fallback_record" ]; then + fallback_record="$remoteproc_record" + fi done <