diff --git a/Runner/suites/Kernel/Baseport/SMP2P_Validation/README.md b/Runner/suites/Kernel/Baseport/SMP2P_Validation/README.md new file mode 100644 index 00000000..618748fd --- /dev/null +++ b/Runner/suites/Kernel/Baseport/SMP2P_Validation/README.md @@ -0,0 +1,48 @@ +# SMP2P validation + +`SMP2P_Validation` validates the upstream Qualcomm Shared Memory Point to +Point driver integration. SMP2P transports state between HLOS and remote +processors through SMEM and an IPC doorbell, and is used by remoteproc stop +and restart flows. + +The suite is passive by design. It does not write SMEM state, enable tracing, +trigger an interrupt, or start, stop, or reset a remote processor. + +## Coverage + +The test runs only when an enabled `qcom,smp2p` node is present in the runtime +device tree. It validates: + +- `CONFIG_QCOM_SMP2P` is enabled. +- Required edge properties: `qcom,smem`, `qcom,local-pid`, and + `qcom,remote-pid`. Interrupt routing is validated through `interrupts` or + `interrupts-extended` when exposed by DT, otherwise through a bound driver + and registered SMP2P interrupt. +- An outgoing doorbell is described using `mboxes` or legacy `qcom,ipc`. +- Each child entry has `qcom,entry-name` and is either an inbound interrupt + controller or an outbound SMEM state provider. +- A runtime platform device is bound to the `qcom_smp2p` driver. +- SMP2P interrupt and upstream tracepoint exposure, when the kernel exposes + them. +- Captured SMP2P and SMEM kernel log health through `scan_dmesg_errors`. + +For LAVA triage, stdout includes the matched kernel-config line, resolved +bound-device sysfs paths, matching `/proc/interrupts` lines, and a readable +preview plus exact hexadecimal bytes for relevant DT properties. The same +runtime evidence is retained in `smp2p_devices.log`, `smp2p_interrupts.log`, +and `smp2p_nodes.log` beside the test result. + +If the platform does not describe SMP2P hardware, the suite returns `SKIP`. + +## Future functional coverage + +The upstream driver has no stable user-space interface for reading or writing +an arbitrary SMP2P entry. A functional test therefore must be tied to a +specific remote processor and must restore its original state. + +The appropriate follow-up is a separate opt-in SSR handshake suite. It can +snapshot tracefs and remoteproc state, restart a user-selected non-critical +remote processor, capture `smp2p_negotiate`, `smp2p_ssr_ack`, +`smp2p_notify_in`, and `smp2p_update_bits`, then restore tracing and the +processor state. It must not be part of this default passive suite because it +can interrupt DSP, modem, or audio services. diff --git a/Runner/suites/Kernel/Baseport/SMP2P_Validation/SMP2P_Validation.yaml b/Runner/suites/Kernel/Baseport/SMP2P_Validation/SMP2P_Validation.yaml new file mode 100644 index 00000000..7b82ccc4 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/SMP2P_Validation/SMP2P_Validation.yaml @@ -0,0 +1,15 @@ +metadata: + name: SMP2P_Validation + format: "Lava-Test Test Definition 1.0" + description: "Validate Qualcomm SMP2P device-tree, driver binding, and kernel health" + os: + - linux + scope: + - functional + +run: + steps: + - REPO_PATH=$PWD + - cd Runner/suites/Kernel/Baseport/SMP2P_Validation + - ./run.sh || true + - $REPO_PATH/Runner/utils/send-to-lava.sh SMP2P_Validation.res diff --git a/Runner/suites/Kernel/Baseport/SMP2P_Validation/run.sh b/Runner/suites/Kernel/Baseport/SMP2P_Validation/run.sh new file mode 100755 index 00000000..fb1af5e7 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/SMP2P_Validation/run.sh @@ -0,0 +1,208 @@ +#!/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 + +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" + +TESTNAME="SMP2P_Validation" +RES_FILE="./$TESTNAME.res" + +# write_result +# Writes the single result line consumed by send-to-lava.sh. Returns the +# status of the write operation. +write_result() { + result="$1" + printf '%s %s\n' "$TESTNAME" "$result" >"$RES_FILE" +} + +# log_dt_property +# Logs a readable preview and exact hexadecimal bytes for one DT property. It +# never changes the DT and returns 0 when the property was logged, otherwise 1. +log_dt_property() { + node_dir="$1" + property="$2" + + if ! property_hex=$(dt_property_hex "$node_dir" "$property"); then + log_info "[SMP2P-DT] node=$node_dir property=$property value=" + return 1 + fi + + property_text=$(dt_property_text "$node_dir" "$property" 2>/dev/null || true) + if [ -n "$property_text" ]; then + log_info "[SMP2P-DT] node=$node_dir property=$property text=$property_text raw_hex=$property_hex" + else + log_info "[SMP2P-DT] node=$node_dir property=$property raw_hex=$property_hex" + fi + return 0 +} + +failures=0 +nodes_file="$SCRIPT_DIR/smp2p_nodes.log" +: >"$nodes_file" + +log_info "--------------------------------------------------------------------------" +log_info "Starting $TESTNAME" + +if ! dt_list_compatible_nodes "qcom,smp2p" >"$nodes_file"; then + log_skip "$TESTNAME SKIP: no enabled qcom,smp2p node is present at runtime" + write_result "SKIP" + exit 0 +fi + +if ! check_kernel_config "CONFIG_QCOM_SMP2P"; then + log_fail "CONFIG_QCOM_SMP2P is not enabled for active SMP2P hardware" + failures=$((failures + 1)) +fi + +if config_evidence=$(kernel_config_value "CONFIG_QCOM_SMP2P"); then + log_info "[SMP2P-CONFIG] $config_evidence" +else + log_info "[SMP2P-CONFIG] CONFIG_QCOM_SMP2P source value is not available" +fi + +runtime_smp2p_bound=0 +if smp2p_runtime_devices >"$SCRIPT_DIR/smp2p_devices.log"; then + runtime_smp2p_bound=1 + while IFS= read -r device_name; do + log_pass "SMP2P platform driver is bound: $device_name" + device_path=$(readlink -f "/sys/bus/platform/drivers/qcom_smp2p/$device_name" 2>/dev/null || true) + [ -n "$device_path" ] || device_path="" + log_info "[SMP2P-DRIVER] device=$device_name sysfs_path=$device_path" + done <"$SCRIPT_DIR/smp2p_devices.log" +else + log_fail "No runtime device is bound to the qcom_smp2p platform driver" + failures=$((failures + 1)) +fi + +smp2p_irq_registered=0 +grep -i "smp2p" /proc/interrupts 2>/dev/null >"$SCRIPT_DIR/smp2p_interrupts.log" || true +if [ -s "$SCRIPT_DIR/smp2p_interrupts.log" ]; then + smp2p_irq_registered=1 + log_pass "SMP2P interrupt registration is visible in /proc/interrupts" + while IFS= read -r interrupt_line; do + log_info "[SMP2P-IRQ] $interrupt_line" + done <"$SCRIPT_DIR/smp2p_interrupts.log" +else + log_info "SMP2P interrupt labels are not exposed by this kernel, driver binding is the runtime evidence" +fi + +while IFS= read -r node_dir; do + [ -n "$node_dir" ] || continue + log_info "SMP2P device-tree node: $node_dir" + for property in \ + compatible \ + status \ + qcom,smem \ + qcom,local-pid \ + qcom,remote-pid \ + interrupts \ + interrupts-extended \ + mboxes \ + qcom,ipc; do + log_dt_property "$node_dir" "$property" || true + done + + if dt_node_has_property "$node_dir" "qcom,smem" && dt_node_has_property "$node_dir" "qcom,local-pid" && dt_node_has_property "$node_dir" "qcom,remote-pid"; then + log_pass "SMP2P node has SMEM and local and remote PID properties" + else + log_fail "SMP2P node is missing required SMEM or PID properties: $node_dir" + failures=$((failures + 1)) + fi + if dt_node_has_property "$node_dir" "interrupts" || \ + dt_node_has_property "$node_dir" "interrupts-extended"; then + log_pass "SMP2P node has interrupt routing" + elif [ "$runtime_smp2p_bound" -eq 1 ] && [ "$smp2p_irq_registered" -eq 1 ]; then + log_info "SMP2P node does not expose direct interrupt properties, runtime driver and IRQ evidence validate routing: $node_dir" + else + log_fail "SMP2P node has no DT or runtime interrupt-routing evidence: $node_dir" + failures=$((failures + 1)) + fi + + if dt_node_has_property "$node_dir" "mboxes" || dt_node_has_property "$node_dir" "qcom,ipc"; then + log_pass "SMP2P node has an outgoing doorbell mechanism" + else + log_fail "SMP2P node is missing both mboxes and qcom,ipc: $node_dir" + failures=$((failures + 1)) + fi + + entry_count=0 + for child_node in "$node_dir"/*; do + [ -d "$child_node" ] || continue + dt_node_has_property "$child_node" "qcom,entry-name" || continue + entry_count=$((entry_count + 1)) + for property in \ + qcom,entry-name \ + interrupt-controller \ + '#interrupt-cells' \ + '#qcom,smem-state-cells'; do + log_dt_property "$child_node" "$property" || true + done + if dt_node_has_property "$child_node" "interrupt-controller" && \ + dt_node_has_property "$child_node" "#interrupt-cells"; then + log_pass "SMP2P inbound entry is valid: ${child_node##*/}" + elif dt_node_has_property "$child_node" "#qcom,smem-state-cells"; then + log_pass "SMP2P outbound entry is valid: ${child_node##*/}" + else + log_fail "SMP2P entry has neither inbound nor outbound semantics: ${child_node##*/}" + failures=$((failures + 1)) + fi + done + if [ "$entry_count" -eq 0 ]; then + log_fail "SMP2P node has no child entries: $node_dir" + failures=$((failures + 1)) + fi +done <"$nodes_file" + +if smp2p_tracepoints_available >"$SCRIPT_DIR/smp2p_tracepoints.log"; then + while IFS= read -r tracepoint; do + log_info "Passive SMP2P tracepoint is available: $tracepoint" + done <"$SCRIPT_DIR/smp2p_tracepoints.log" +else + log_info "SMP2P tracepoints are not enabled in this kernel configuration" +fi + +scan_dmesg_errors "$SCRIPT_DIR" "smp2p|qcom_smp2p|qcom-smem" "not a crash|subsys-restart" || true +if [ -s "$SCRIPT_DIR/dmesg_errors.log" ]; then + log_fail "SMP2P and SMEM kernel errors are recorded in dmesg_errors.log" + failures=$((failures + 1)) +fi + +if [ "$failures" -eq 0 ]; then + log_pass "$TESTNAME PASS" + write_result "PASS" +else + log_fail "$TESTNAME FAIL: failures=$failures" + write_result "FAIL" +fi +exit 0 diff --git a/Runner/suites/Kernel/Baseport/remoteproc/README.md b/Runner/suites/Kernel/Baseport/remoteproc/README.md new file mode 100644 index 00000000..82cf7320 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/remoteproc/README.md @@ -0,0 +1,14 @@ +# Remoteproc validation + +`remoteproc` is the unified passive health test for all registered remote +processors. It replaces the separate PIL remoteproc smoke test because both +tests inspected the same runtime remoteproc state. + +The suite validates required sysfs attributes, accepts valid `running`, +`attached`, and non-autoboot `offline` states, rejects `crashed` states, +records the bound driver, checks image-provided firmware when it is exposed, +and captures relevant remoteproc and Qualcomm PAS kernel errors. + +The test does not start, stop, or reset a remote processor. SMP2P remains a +separate suite because it validates SMEM edge configuration, doorbell routing, +interrupt registration, and the `qcom_smp2p` driver. diff --git a/Runner/suites/Kernel/Baseport/remoteproc/run.sh b/Runner/suites/Kernel/Baseport/remoteproc/run.sh index 1018061a..c4c858bc 100755 --- a/Runner/suites/Kernel/Baseport/remoteproc/run.sh +++ b/Runner/suites/Kernel/Baseport/remoteproc/run.sh @@ -2,10 +2,15 @@ # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. # SPDX-License-Identifier: BSD-3-Clause -# Robustly find and source init_env -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + +# ---------- 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" @@ -19,78 +24,92 @@ if [ -z "$INIT_ENV" ]; then exit 1 fi -# Only source if not already loaded (idempotent) if [ -z "${__INIT_ENV_LOADED:-}" ]; then # shellcheck disable=SC1090 . "$INIT_ENV" + __INIT_ENV_LOADED=1 fi -# Always source functestlib.sh, using $TOOLS exported by init_env -# shellcheck disable=SC1090,SC1091 + +# shellcheck disable=SC1090 +. "$INIT_ENV" +# shellcheck disable=SC1091 . "$TOOLS/functestlib.sh" TESTNAME="remoteproc" -test_path=$(find_test_case_by_name "$TESTNAME") -cd "$test_path" || exit 1 -# shellcheck disable=SC2034 -res_file="./$TESTNAME.res" +RES_FILE="./$TESTNAME.res" -log_info "-----------------------------------------------------------------------------------------" -log_info "-------------------Starting $TESTNAME Testcase----------------------------" -log_info "=== Test Initialization ===" +# write_result +# Writes the single result line consumed by send-to-lava.sh. Returns the +# status of the write operation. +write_result() { + result="$1" + printf '%s %s\n' "$TESTNAME" "$result" >"$RES_FILE" +} -detect_platform +failures=0 +runtime_file="$SCRIPT_DIR/remoteproc_runtime.log" +: >"$runtime_file" -if [ ! -d "/sys/class/remoteproc" ]; then - log_skip "$TESTNAME : remoteproc sysfs not found (/sys/class/remoteproc missing), skipping test" - echo "$TESTNAME SKIP" > "$res_file" - exit 0 -fi +log_info "--------------------------------------------------------------------------" +log_info "Starting $TESTNAME" -rproc_count=$(find /sys/class/remoteproc -maxdepth 1 -name "remoteproc*" 2>/dev/null | wc -l) -if [ "$rproc_count" -eq 0 ]; then - log_skip "$TESTNAME : No remoteproc entries found under /sys/class/remoteproc, skipping test" - echo "$TESTNAME SKIP" > "$res_file" +if ! list_remoteproc_instances "$runtime_file"; then + log_skip "$TESTNAME SKIP: no runtime remoteproc instance is registered" + write_result "SKIP" exit 0 fi -all_pass=true - -# Iterate over each remoteproc instance -for rproc_dir in /sys/class/remoteproc/remoteproc*; do - rproc_name=$(basename "$rproc_dir") - firmware=$(cat "$rproc_dir/firmware" 2>/dev/null) - state=$(cat "$rproc_dir/state" 2>/dev/null) +while IFS='|' read -r remoteproc_path remoteproc_name remoteproc_firmware remoteproc_state; do + [ -n "$remoteproc_path" ] || continue - # Skip modem subsystem on Kodiak platform - if printf '%s' "$firmware" | grep -q "modem" && [ "${PLATFORM_TARGET}" = "Kodiak" ]; then - log_info "Skipping modem subsystem ($rproc_name) on Kodiak platform" - continue + remoteproc_driver="" + if [ -e "$remoteproc_path/device/driver" ]; then + remoteproc_driver=$(basename "$(readlink -f "$remoteproc_path/device/driver")") fi - # soccp is expected to be in 'attached' state, all others in 'running' state - if printf '%s' "$firmware" | grep -q "soccp"; then - expected_state="attached" - else - expected_state="running" + log_info "Remoteproc $(basename "$remoteproc_path"), driver=$remoteproc_driver, name=$remoteproc_name, firmware=$remoteproc_firmware, state=$remoteproc_state" + + if [ -z "$remoteproc_name" ] || [ -z "$remoteproc_firmware" ] || [ "$remoteproc_state" = "unknown" ]; then + log_fail "Remoteproc sysfs attributes are incomplete for $remoteproc_path" + failures=$((failures + 1)) + continue fi - log_info "$rproc_name | firmware: $firmware | state: $state | expected: $expected_state" + case "$remoteproc_state" in + running|attached) + log_pass "Remoteproc state is valid: $remoteproc_state" + ;; + offline) + log_pass "Remoteproc is offline, valid for a non-autoboot processor: $remoteproc_name" + ;; + crashed) + log_fail "Remoteproc is crashed: $remoteproc_name" + failures=$((failures + 1)) + ;; + *) + log_fail "Remoteproc has an unexpected state: $remoteproc_state" + failures=$((failures + 1)) + ;; + esac - if [ "$state" = "$expected_state" ]; then - log_info "$rproc_name is in expected state '$expected_state' : PASS" + if firmware_path=$(find_image_firmware "$remoteproc_firmware"); then + log_pass "Image-provided firmware is present: $firmware_path" else - log_fail "$rproc_name is in state '$state', expected '$expected_state' : FAIL" - all_pass=false + log_info "Firmware is not exposed below standard firmware paths: $remoteproc_firmware" fi -done +done <"$runtime_file" -# Print overall test result -if [ "$all_pass" = "true" ]; then - log_pass "$TESTNAME : Test Passed" - echo "$TESTNAME PASS" > "$res_file" - exit 0 +scan_dmesg_errors "$SCRIPT_DIR" "remoteproc|qcom.*pas|qcom_q6v5" "subsys-restart|not a crash|firmware.*already" || true +if [ -s "$SCRIPT_DIR/dmesg_errors.log" ]; then + log_fail "Remoteproc and Qualcomm PAS kernel errors are recorded in dmesg_errors.log" + failures=$((failures + 1)) +fi + +if [ "$failures" -eq 0 ]; then + log_pass "$TESTNAME PASS" + write_result "PASS" else - log_fail "$TESTNAME : Test Failed" - echo "$TESTNAME FAIL" > "$res_file" - exit 1 + log_fail "$TESTNAME FAIL: failures=$failures" + write_result "FAIL" fi +exit 0 diff --git a/Runner/utils/functestlib.sh b/Runner/utils/functestlib.sh index d113169b..a259f794 100755 --- a/Runner/utils/functestlib.sh +++ b/Runner/utils/functestlib.sh @@ -819,6 +819,24 @@ check_kernel_config() { return 0 } +############################################################################### +# kernel_config_value +# Prints the active CONFIG_NAME=value line from /proc/config.gz. Returns 0 +# when found, 1 when it is absent or the kernel config cannot be read, and 3 +# when the config name is omitted. +############################################################################### +kernel_config_value() { + config_name="$1" + [ -n "$config_name" ] || return 3 + [ -r /proc/config.gz ] || return 1 + + if command -v zgrep >/dev/null 2>&1; then + zgrep -m 1 -E "^${config_name}=" /proc/config.gz 2>/dev/null + else + gzip -dc /proc/config.gz 2>/dev/null | grep -m 1 -E "^${config_name}=" + fi +} + check_dt_nodes() { node_paths="$1" log_info "$node_paths" @@ -3065,8 +3083,14 @@ dt_has_remoteproc_fw() { fw="$1" [ -n "$fw" ] || return 3 - base="/proc/device-tree" - [ -d "$base" ] || return 1 + base="" + for candidate in /proc/device-tree /sys/firmware/devicetree/base; do + if [ -d "$candidate" ]; then + base="$candidate" + break + fi + done + [ -n "$base" ] || return 1 # lower-case match key fw_lc=$(printf '%s\n' "$fw" | tr '[:upper:]' '[:lower:]') @@ -3096,6 +3120,182 @@ dt_has_remoteproc_fw() { return 1 } +############################################################################### +# dt_list_compatible_nodes +# Prints one enabled device-tree node directory per line from both standard DT +# roots. Returns 0 when at least one node is found, 1 when none are found, +# and 3 when the compatible substring is omitted. +############################################################################### +dt_list_compatible_nodes() { + compatible="$1" + [ -n "$compatible" ] || return 3 + + found=0 + previous_root="" + for dt_root in /proc/device-tree /sys/firmware/devicetree/base; do + [ -d "$dt_root" ] || continue + resolved_root=$(readlink -f "$dt_root" 2>/dev/null) + [ -n "$resolved_root" ] || resolved_root="$dt_root" + [ "$resolved_root" = "$previous_root" ] && continue + previous_root="$resolved_root" + + matches_file=$(mktemp "${TMPDIR:-/tmp}/dt-compatible.XXXXXX") || return 1 + grep -r -l -a -F "$compatible" "$dt_root" 2>/dev/null >"$matches_file" || true + while IFS= read -r compatible_file || [ -n "$compatible_file" ]; do + [ -n "$compatible_file" ] || continue + node_dir=$(dirname "$compatible_file") + [ -r "$node_dir/status" ] && IFS= read -r node_status <"$node_dir/status" + case "${node_status:-okay}" in + disabled|fail|failed) + continue + ;; + esac + printf '%s\n' "$node_dir" + found=1 + done <"$matches_file" + rm -f "$matches_file" + done + [ "$found" -eq 1 ] +} + +############################################################################### +# dt_node_has_property +# Returns 0 if the exact DT property file exists, otherwise 1. The function +# does not interpret property values. +############################################################################### +dt_node_has_property() { + node_dir="$1" + property="$2" + [ -n "$node_dir" ] && [ -n "$property" ] || return 3 + [ -e "$node_dir/$property" ] +} + +############################################################################### +# dt_property_hex +# Prints the exact device-tree property bytes as one lowercase hexadecimal +# string. Returns 0 when the property is readable, 1 when absent or unreadable, +# and 3 when either argument is omitted. +############################################################################### +dt_property_hex() { + node_dir="$1" + property="$2" + [ -n "$node_dir" ] && [ -n "$property" ] || return 3 + [ -r "$node_dir/$property" ] || return 1 + od -An -v -tx1 "$node_dir/$property" 2>/dev/null | tr -d ' \n' +} + +############################################################################### +# dt_property_text +# Prints NUL-separated text values from a device-tree property as one +# space-separated, printable line. Returns 0 for a readable property, 1 when +# absent or unreadable, and 3 when either argument is omitted. +############################################################################### +dt_property_text() { + node_dir="$1" + property="$2" + [ -n "$node_dir" ] && [ -n "$property" ] || return 3 + [ -r "$node_dir/$property" ] || return 1 + tr '\000' ' ' <"$node_dir/$property" | tr -cd '[:print:] \n' | sed 's/[[:space:]]*$//' +} + +############################################################################### +# list_remoteproc_instances [outfile] +# Prints or appends '|||' for every runtime +# remoteproc. Returns 0 if one or more instances exist, otherwise 1. +############################################################################### +list_remoteproc_instances() { + outfile="$1" + found=0 + for remoteproc_path in /sys/class/remoteproc/remoteproc*; do + [ -d "$remoteproc_path" ] || continue + remoteproc_name="" + remoteproc_firmware="" + remoteproc_state="unknown" + [ -r "$remoteproc_path/name" ] && IFS= read -r remoteproc_name <"$remoteproc_path/name" + [ -r "$remoteproc_path/firmware" ] && IFS= read -r remoteproc_firmware <"$remoteproc_path/firmware" + [ -r "$remoteproc_path/state" ] && IFS= read -r remoteproc_state <"$remoteproc_path/state" + if [ -n "$outfile" ]; then + printf '%s|%s|%s|%s\n' "$remoteproc_path" "$remoteproc_name" "$remoteproc_firmware" "$remoteproc_state" >>"$outfile" + else + printf '%s|%s|%s|%s\n' "$remoteproc_path" "$remoteproc_name" "$remoteproc_firmware" "$remoteproc_state" + fi + found=1 + done + [ "$found" -eq 1 ] +} + +############################################################################### +# find_image_firmware +# Prints the first matching image-provided firmware path under the standard +# firmware roots, accepting uncompressed, .xz, and .zst files. Returns 0 on a +# match, 1 when no asset is exposed, and 3 when no name is supplied. +############################################################################### +find_image_firmware() { + firmware_name="$1" + [ -n "$firmware_name" ] || return 3 + + for firmware_root in /lib/firmware /usr/lib/firmware; do + for firmware_path in \ + "$firmware_root/$firmware_name" \ + "$firmware_root/$firmware_name.xz" \ + "$firmware_root/$firmware_name.zst"; do + if [ -f "$firmware_path" ]; then + printf '%s\n' "$firmware_path" + return 0 + fi + done + done + return 1 +} + +############################################################################### +# smp2p_runtime_devices +# Prints bound platform-device names owned by the qcom_smp2p driver. Returns 0 +# if at least one device is bound, otherwise 1. +############################################################################### +smp2p_runtime_devices() { + driver_dir="/sys/bus/platform/drivers/qcom_smp2p" + [ -d "$driver_dir" ] || return 1 + found=0 + for device_link in "$driver_dir"/*; do + [ -e "$device_link" ] || continue + case "$(basename "$device_link")" in + bind|unbind|uevent|module) + continue + ;; + esac + basename "$(readlink -f "$device_link")" + found=1 + done + [ "$found" -eq 1 ] +} + +############################################################################### +# smp2p_tracepoints_available +# Prints available upstream SMP2P tracepoint names from tracefs. Returns 0 if +# at least one event is exposed, otherwise 1. It only inspects tracefs and +# never enables tracing or changes the tracing buffer. +############################################################################### +smp2p_tracepoints_available() { + trace_root="" + for candidate in /sys/kernel/tracing /sys/kernel/debug/tracing; do + if [ -d "$candidate/events/smp2p" ]; then + trace_root="$candidate" + break + fi + done + [ -n "$trace_root" ] || return 1 + + found=0 + for tracepoint in smp2p_negotiate smp2p_ssr_ack smp2p_notify_in smp2p_update_bits; do + if [ -d "$trace_root/events/smp2p/$tracepoint" ]; then + printf '%s\n' "$tracepoint" + found=1 + fi + done + [ "$found" -eq 1 ] +} + # Find the remoteproc path for a given firmware substring (e.g., "adsp", "cdsp", "gdsp"). # Logic: # - grep -n over /sys/class/remoteproc/remoteproc*/firmware (one line per remoteproc)